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










29















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) ", 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. (Fixed)




Solved with File's Owner, ViewController base class (clicked left outline menu.)




2.issue: Header height problem (Fixed)




Solved adding headerView.clipsToBounds = true in viewForHeaderInSection: method.



For constraint warnings this answer solved my problems:



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. -fixed-



self.automaticallyAdjustsScrollViewInsets = false



3.issue: If button under View (Fixed)




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

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













share|improve this question
























  • Try looking here: stackoverflow.com/questions/9219234/…

    – Curmudgeonlybumbly
    Apr 28 '16 at 22:49











  • FWIW, the accepted answer to that other question is a kludgy solution that was used before iOS offered dequeue capabilities for header/footer views.

    – Rob
    Apr 29 '16 at 6:41















29















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) ", 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. (Fixed)




Solved with File's Owner, ViewController base class (clicked left outline menu.)




2.issue: Header height problem (Fixed)




Solved adding headerView.clipsToBounds = true in viewForHeaderInSection: method.



For constraint warnings this answer solved my problems:



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. -fixed-



self.automaticallyAdjustsScrollViewInsets = false



3.issue: If button under View (Fixed)




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

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













share|improve this question
























  • Try looking here: stackoverflow.com/questions/9219234/…

    – Curmudgeonlybumbly
    Apr 28 '16 at 22:49











  • FWIW, the accepted answer to that other question is a kludgy solution that was used before iOS offered dequeue capabilities for header/footer views.

    – Rob
    Apr 29 '16 at 6:41













29












29








29


13






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) ", 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. (Fixed)




Solved with File's Owner, ViewController base class (clicked left outline menu.)




2.issue: Header height problem (Fixed)




Solved adding headerView.clipsToBounds = true in viewForHeaderInSection: method.



For constraint warnings this answer solved my problems:



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. -fixed-



self.automaticallyAdjustsScrollViewInsets = false



3.issue: If button under View (Fixed)




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

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













share|improve this question
















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) ", 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. (Fixed)




Solved with File's Owner, ViewController base class (clicked left outline menu.)




2.issue: Header height problem (Fixed)




Solved adding headerView.clipsToBounds = true in viewForHeaderInSection: method.



For constraint warnings this answer solved my problems:



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. -fixed-



self.automaticallyAdjustsScrollViewInsets = false



3.issue: If button under View (Fixed)




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

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










ios swift uitableview xib






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited May 23 '17 at 11:33









Community

11




11










asked Apr 28 '16 at 22:33









burakgunduzburakgunduz

1,14031643




1,14031643












  • Try looking here: stackoverflow.com/questions/9219234/…

    – Curmudgeonlybumbly
    Apr 28 '16 at 22:49











  • FWIW, the accepted answer to that other question is a kludgy solution that was used before iOS offered dequeue capabilities for header/footer views.

    – Rob
    Apr 29 '16 at 6:41

















  • Try looking here: stackoverflow.com/questions/9219234/…

    – Curmudgeonlybumbly
    Apr 28 '16 at 22:49











  • FWIW, the accepted answer to that other question is a kludgy solution that was used before iOS offered dequeue capabilities for header/footer views.

    – Rob
    Apr 29 '16 at 6:41
















Try looking here: stackoverflow.com/questions/9219234/…

– Curmudgeonlybumbly
Apr 28 '16 at 22:49





Try looking here: stackoverflow.com/questions/9219234/…

– Curmudgeonlybumbly
Apr 28 '16 at 22:49













FWIW, the accepted answer to that other question is a kludgy solution that was used before iOS offered dequeue capabilities for header/footer views.

– Rob
Apr 29 '16 at 6:41





FWIW, the accepted answer to that other question is a kludgy solution that was used before iOS offered dequeue capabilities for header/footer views.

– Rob
Apr 29 '16 at 6:41












4 Answers
4






active

oldest

votes


















73














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. Likewise, you may want to specify a protocol by which the header can inform the view controller of events (like the tapping of the button). Thus, in Swift 3 and later:



    // 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)






  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;


    • Hook up the button to the @IBAction; and


    • For the root view in the NIB, make sure to set the background color to "default" or else you'll get annoying warnings about changing background colors.




  3. In the viewDidLoad in the view controller, register the NIB. In Swift 3 and later:



    override func viewDidLoad() 
    super.viewDidLoad()

    tableView.register(UINib(nibName: "CustomHeader", bundle: nil), forHeaderFooterViewReuseIdentifier: CustomHeader.reuseIdentifier)




  4. 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. The only think you need to do (for the protocol for the button to work) is to specify its delegate. For example, in 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




  5. Obviously, if you're going to specify the view controller as the delegate for the button in the header view, you have to conform to that protocol:



    extension ViewController: CustomHeaderDelegate 
    func customHeader(_ customHeader: CustomHeader, didTapButtonInSection section: Int)
    print("did tap button", section)




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.




In matt's answer, he protests:




The problem, quite simply, is that you cannot magically turn a UIView in a nib into a UITableViewHeaderFooterView merely by declaring it so in the Identity inspector.




This is simply not correct. If you use the above NIB-based approach, the class that is instantiated for the root view of this header view is a UITableViewHeaderFooterView subclass, not a UIView. It instantiates whatever class you specify for the base class for the NIBs root view.



What is correct, though, is that some of the properties for this class (notably the contentView) aren't used in this NIB based approach. It really should be optional property, just like textLabel and detailTextLabel are (or, better, they should add proper support for UITableViewHeaderFooterView in IB). I agree that this is poor design on Apple's part, but it strikes me as a sloppy, idiosyncratic detail, but a minor issue given all the problems in table views. E.g., it is extraordinary that after all these years, that we still can't do prototype header/footer views in storyboards at all and have to rely on these NIB and class registration techniques at all.



But, it is incorrect to conclude that one cannot use register(_:forHeaderFooterViewReuseIdentifier:), an API method that has actively been in use since iOS 6. Let’s not throw the baby out with the bath water.




See previous revision of this answer for Swift 2 renditions.






share|improve this answer




















  • 2





    Did you set the File Owner of the NIB to be your view controller class? If so, when you select the button, the view controller should be one of the two classes that are available under the "Automatic" setting in the assistant editor.

    – Rob
    Apr 29 '16 at 13:00






  • 1





    I'd suggest you examine your view hierarchy (eg using the view debugger) and just see what the button's superview is. Did you, for example, put the button within a container view of the CustomHeader?

    – Rob
    Apr 29 '16 at 14:19






  • 2





    I think it is worth mentioning: add 'self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension' and 'self.tableView.estimatedSectionHeaderHeight = 70' to 'viewDidLoad'. If your constraints are pining each subview on all sides, you get Self-Sized headers

    – Torsten B
    Aug 5 '16 at 13:31







  • 1





    Thank you for detailed answer! ;)

    – Tom Calmon
    Aug 23 '16 at 19:34






  • 1





    To restate what @Rob said in an above comment, ensure that when you hook your IBOutlet to your xib that you select the name of the custom class, not File Owner.

    – Jacob Davis
    Feb 4 '18 at 21:09



















24














Rob's answer, though it sounds convincing and has withstood the test of time, is wrong and always was. It's difficult to stand alone against the overwhelming crowd "wisdom" of acceptance and numerous upvotes, but I'll try to summon the courage to tell the truth.



The problem, quite simply, is that you cannot magically turn a UIView in a nib into a UITableViewHeaderFooterView merely by declaring it so in the Identity inspector. A UITableViewHeaderFooterView has important features that are key to its correct operation, and a plain UIView, no matter how you may cast it, lacks them.




  • A UITableViewHeaderFooterView has a contentView, and all your custom subviews must be added to this, not to the UITableViewHeaderFooterView.



    But a UIView mysteriously cast as a UITableViewHeaderFooterView lacks this contentView in the nib. Thus, when Rob says "Add your controls and constraints in IB", he is having you add subviews directly to the UITableViewHeaderFooterView, and not to its contentView. The header thus ends up incorrectly configured.




  • Another sign of the issue is that you are not permitted to give a UITableViewHeaderFooterView a background color. If you do, you'll get this message in the console:




    Setting the background color on UITableViewHeaderFooterView has been deprecated. Please set a custom UIView with your desired background color to the backgroundView property instead.




    But in the nib, you cannot help setting a background color on your UITableViewHeaderFooterView, and you do get that message in the console.



So what's the right answer to the question? There's no possible answer. Apple has made a huge goof here. They have provided a method that allows you to register a nib as the source of your UITableViewHeaderFooterView, but there is no UITableViewHeaderFooterView in the Object Library. Therefore this method is useless. It is impossible to design a UITableViewHeaderFooterView correctly in a nib.



This is a huge bug in Xcode. I filed a bug report on this matter in 2013 and it is still sitting there, open. I refile the bug year after year, and Apple keeps pushing back, saying "It has not been determined how or when the issue will be resolved." So they acknowledge the bug, but they do nothing about it.



What you can do, however, is design a normal UIView in the nib, and then, in code (in your implementation of viewForHeaderInSection), load the view manually from the nib and stuff it into the contentView of your header view.



For example, let's say we want to design our header in the nib, and we have a label in the header to which we want to connect an outlet lab. Then we need both a custom header class and a custom view class:



class MyHeaderView : UITableViewHeaderFooterView 
weak var content : MyHeaderViewContent!

class MyHeaderViewContent : UIView
@IBOutlet weak var lab : UILabel!



We register our header view's class, not the nib:



self.tableView.register(MyHeaderView.self,
forHeaderFooterViewReuseIdentifier: self.headerID)


In the view xib file, we declare our view to be a MyHeaderViewContent — not a MyHeaderView.



In viewForHeaderInSection, we pluck the view out of the nib, stuff it into the contentView of the header, and configure the reference to it:



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



It's dreadful and annoying, but it is the best you can do.






share|improve this answer

























  • Why do you need MyHeaderView at all? Instead of subclassing UITableViewHeaderFooterView (which isn't recommended by Apple - "You can use this class as-is without subclassing in most cases.") you can register it directly - tableView.register(UITableViewHeaderFooterView.self, ...). Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.

    – Piotr
    Jun 16 '18 at 20:40












  • Great answer btw. Good job on trying to understand Apple's mess :)

    – Piotr
    Jun 16 '18 at 20:42











  • @Piotr “Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.” And crash? Why would that be good?

    – matt
    Jun 16 '18 at 21:03











  • Also “You can use this class as-is without subclassing in most cases.” is not a recommendation against subclassing. I subclass it just fine.

    – matt
    Jun 16 '18 at 21:05











  • contentView.subviews[0] as! will work

    – Piotr
    Jun 16 '18 at 21:24


















0














I don't have enough reputation to add comment to Matt answer.



Anyway, the only thing missing here is to remove all subviews from UITableViewHeaderFooterView.contentView before adding new views. This will reset reused cell to initial state and avoid memory leak.






share|improve this answer






























    -1














    Rob's answers seems right but Matt has provided a great details how Rob’s answer is wrong. While on the other hand Apple used Rob’s method in there example.
    Follow this link.



    https://developer.apple.com/library/archive/samplecode/TableViewUpdates/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010139-Intro-DontLinkElementID_2






    share|improve this answer























    • - Hello down voter, please give the reason.

      – AsifHabib
      Feb 26 at 18:47










    Your Answer






    StackExchange.ifUsing("editor", function ()
    StackExchange.using("externalEditor", function ()
    StackExchange.using("snippets", function ()
    StackExchange.snippets.init();
    );
    );
    , "code-snippets");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "1"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader:
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    ,
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













    draft saved

    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f36926612%2fswift-how-creating-custom-viewforheaderinsection-using-a-xib-file%23new-answer', 'question_page');

    );

    Post as a guest















    Required, but never shown

























    4 Answers
    4






    active

    oldest

    votes








    4 Answers
    4






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    73














    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. Likewise, you may want to specify a protocol by which the header can inform the view controller of events (like the tapping of the button). Thus, in Swift 3 and later:



      // 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)






    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;


      • Hook up the button to the @IBAction; and


      • For the root view in the NIB, make sure to set the background color to "default" or else you'll get annoying warnings about changing background colors.




    3. In the viewDidLoad in the view controller, register the NIB. In Swift 3 and later:



      override func viewDidLoad() 
      super.viewDidLoad()

      tableView.register(UINib(nibName: "CustomHeader", bundle: nil), forHeaderFooterViewReuseIdentifier: CustomHeader.reuseIdentifier)




    4. 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. The only think you need to do (for the protocol for the button to work) is to specify its delegate. For example, in 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




    5. Obviously, if you're going to specify the view controller as the delegate for the button in the header view, you have to conform to that protocol:



      extension ViewController: CustomHeaderDelegate 
      func customHeader(_ customHeader: CustomHeader, didTapButtonInSection section: Int)
      print("did tap button", section)




    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.




    In matt's answer, he protests:




    The problem, quite simply, is that you cannot magically turn a UIView in a nib into a UITableViewHeaderFooterView merely by declaring it so in the Identity inspector.




    This is simply not correct. If you use the above NIB-based approach, the class that is instantiated for the root view of this header view is a UITableViewHeaderFooterView subclass, not a UIView. It instantiates whatever class you specify for the base class for the NIBs root view.



    What is correct, though, is that some of the properties for this class (notably the contentView) aren't used in this NIB based approach. It really should be optional property, just like textLabel and detailTextLabel are (or, better, they should add proper support for UITableViewHeaderFooterView in IB). I agree that this is poor design on Apple's part, but it strikes me as a sloppy, idiosyncratic detail, but a minor issue given all the problems in table views. E.g., it is extraordinary that after all these years, that we still can't do prototype header/footer views in storyboards at all and have to rely on these NIB and class registration techniques at all.



    But, it is incorrect to conclude that one cannot use register(_:forHeaderFooterViewReuseIdentifier:), an API method that has actively been in use since iOS 6. Let’s not throw the baby out with the bath water.




    See previous revision of this answer for Swift 2 renditions.






    share|improve this answer




















    • 2





      Did you set the File Owner of the NIB to be your view controller class? If so, when you select the button, the view controller should be one of the two classes that are available under the "Automatic" setting in the assistant editor.

      – Rob
      Apr 29 '16 at 13:00






    • 1





      I'd suggest you examine your view hierarchy (eg using the view debugger) and just see what the button's superview is. Did you, for example, put the button within a container view of the CustomHeader?

      – Rob
      Apr 29 '16 at 14:19






    • 2





      I think it is worth mentioning: add 'self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension' and 'self.tableView.estimatedSectionHeaderHeight = 70' to 'viewDidLoad'. If your constraints are pining each subview on all sides, you get Self-Sized headers

      – Torsten B
      Aug 5 '16 at 13:31







    • 1





      Thank you for detailed answer! ;)

      – Tom Calmon
      Aug 23 '16 at 19:34






    • 1





      To restate what @Rob said in an above comment, ensure that when you hook your IBOutlet to your xib that you select the name of the custom class, not File Owner.

      – Jacob Davis
      Feb 4 '18 at 21:09
















    73














    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. Likewise, you may want to specify a protocol by which the header can inform the view controller of events (like the tapping of the button). Thus, in Swift 3 and later:



      // 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)






    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;


      • Hook up the button to the @IBAction; and


      • For the root view in the NIB, make sure to set the background color to "default" or else you'll get annoying warnings about changing background colors.




    3. In the viewDidLoad in the view controller, register the NIB. In Swift 3 and later:



      override func viewDidLoad() 
      super.viewDidLoad()

      tableView.register(UINib(nibName: "CustomHeader", bundle: nil), forHeaderFooterViewReuseIdentifier: CustomHeader.reuseIdentifier)




    4. 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. The only think you need to do (for the protocol for the button to work) is to specify its delegate. For example, in 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




    5. Obviously, if you're going to specify the view controller as the delegate for the button in the header view, you have to conform to that protocol:



      extension ViewController: CustomHeaderDelegate 
      func customHeader(_ customHeader: CustomHeader, didTapButtonInSection section: Int)
      print("did tap button", section)




    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.




    In matt's answer, he protests:




    The problem, quite simply, is that you cannot magically turn a UIView in a nib into a UITableViewHeaderFooterView merely by declaring it so in the Identity inspector.




    This is simply not correct. If you use the above NIB-based approach, the class that is instantiated for the root view of this header view is a UITableViewHeaderFooterView subclass, not a UIView. It instantiates whatever class you specify for the base class for the NIBs root view.



    What is correct, though, is that some of the properties for this class (notably the contentView) aren't used in this NIB based approach. It really should be optional property, just like textLabel and detailTextLabel are (or, better, they should add proper support for UITableViewHeaderFooterView in IB). I agree that this is poor design on Apple's part, but it strikes me as a sloppy, idiosyncratic detail, but a minor issue given all the problems in table views. E.g., it is extraordinary that after all these years, that we still can't do prototype header/footer views in storyboards at all and have to rely on these NIB and class registration techniques at all.



    But, it is incorrect to conclude that one cannot use register(_:forHeaderFooterViewReuseIdentifier:), an API method that has actively been in use since iOS 6. Let’s not throw the baby out with the bath water.




    See previous revision of this answer for Swift 2 renditions.






    share|improve this answer




















    • 2





      Did you set the File Owner of the NIB to be your view controller class? If so, when you select the button, the view controller should be one of the two classes that are available under the "Automatic" setting in the assistant editor.

      – Rob
      Apr 29 '16 at 13:00






    • 1





      I'd suggest you examine your view hierarchy (eg using the view debugger) and just see what the button's superview is. Did you, for example, put the button within a container view of the CustomHeader?

      – Rob
      Apr 29 '16 at 14:19






    • 2





      I think it is worth mentioning: add 'self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension' and 'self.tableView.estimatedSectionHeaderHeight = 70' to 'viewDidLoad'. If your constraints are pining each subview on all sides, you get Self-Sized headers

      – Torsten B
      Aug 5 '16 at 13:31







    • 1





      Thank you for detailed answer! ;)

      – Tom Calmon
      Aug 23 '16 at 19:34






    • 1





      To restate what @Rob said in an above comment, ensure that when you hook your IBOutlet to your xib that you select the name of the custom class, not File Owner.

      – Jacob Davis
      Feb 4 '18 at 21:09














    73












    73








    73







    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. Likewise, you may want to specify a protocol by which the header can inform the view controller of events (like the tapping of the button). Thus, in Swift 3 and later:



      // 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)






    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;


      • Hook up the button to the @IBAction; and


      • For the root view in the NIB, make sure to set the background color to "default" or else you'll get annoying warnings about changing background colors.




    3. In the viewDidLoad in the view controller, register the NIB. In Swift 3 and later:



      override func viewDidLoad() 
      super.viewDidLoad()

      tableView.register(UINib(nibName: "CustomHeader", bundle: nil), forHeaderFooterViewReuseIdentifier: CustomHeader.reuseIdentifier)




    4. 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. The only think you need to do (for the protocol for the button to work) is to specify its delegate. For example, in 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




    5. Obviously, if you're going to specify the view controller as the delegate for the button in the header view, you have to conform to that protocol:



      extension ViewController: CustomHeaderDelegate 
      func customHeader(_ customHeader: CustomHeader, didTapButtonInSection section: Int)
      print("did tap button", section)




    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.




    In matt's answer, he protests:




    The problem, quite simply, is that you cannot magically turn a UIView in a nib into a UITableViewHeaderFooterView merely by declaring it so in the Identity inspector.




    This is simply not correct. If you use the above NIB-based approach, the class that is instantiated for the root view of this header view is a UITableViewHeaderFooterView subclass, not a UIView. It instantiates whatever class you specify for the base class for the NIBs root view.



    What is correct, though, is that some of the properties for this class (notably the contentView) aren't used in this NIB based approach. It really should be optional property, just like textLabel and detailTextLabel are (or, better, they should add proper support for UITableViewHeaderFooterView in IB). I agree that this is poor design on Apple's part, but it strikes me as a sloppy, idiosyncratic detail, but a minor issue given all the problems in table views. E.g., it is extraordinary that after all these years, that we still can't do prototype header/footer views in storyboards at all and have to rely on these NIB and class registration techniques at all.



    But, it is incorrect to conclude that one cannot use register(_:forHeaderFooterViewReuseIdentifier:), an API method that has actively been in use since iOS 6. Let’s not throw the baby out with the bath water.




    See previous revision of this answer for Swift 2 renditions.






    share|improve this 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. Likewise, you may want to specify a protocol by which the header can inform the view controller of events (like the tapping of the button). Thus, in Swift 3 and later:



      // 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)






    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;


      • Hook up the button to the @IBAction; and


      • For the root view in the NIB, make sure to set the background color to "default" or else you'll get annoying warnings about changing background colors.




    3. In the viewDidLoad in the view controller, register the NIB. In Swift 3 and later:



      override func viewDidLoad() 
      super.viewDidLoad()

      tableView.register(UINib(nibName: "CustomHeader", bundle: nil), forHeaderFooterViewReuseIdentifier: CustomHeader.reuseIdentifier)




    4. 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. The only think you need to do (for the protocol for the button to work) is to specify its delegate. For example, in 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




    5. Obviously, if you're going to specify the view controller as the delegate for the button in the header view, you have to conform to that protocol:



      extension ViewController: CustomHeaderDelegate 
      func customHeader(_ customHeader: CustomHeader, didTapButtonInSection section: Int)
      print("did tap button", section)




    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.




    In matt's answer, he protests:




    The problem, quite simply, is that you cannot magically turn a UIView in a nib into a UITableViewHeaderFooterView merely by declaring it so in the Identity inspector.




    This is simply not correct. If you use the above NIB-based approach, the class that is instantiated for the root view of this header view is a UITableViewHeaderFooterView subclass, not a UIView. It instantiates whatever class you specify for the base class for the NIBs root view.



    What is correct, though, is that some of the properties for this class (notably the contentView) aren't used in this NIB based approach. It really should be optional property, just like textLabel and detailTextLabel are (or, better, they should add proper support for UITableViewHeaderFooterView in IB). I agree that this is poor design on Apple's part, but it strikes me as a sloppy, idiosyncratic detail, but a minor issue given all the problems in table views. E.g., it is extraordinary that after all these years, that we still can't do prototype header/footer views in storyboards at all and have to rely on these NIB and class registration techniques at all.



    But, it is incorrect to conclude that one cannot use register(_:forHeaderFooterViewReuseIdentifier:), an API method that has actively been in use since iOS 6. Let’s not throw the baby out with the bath water.




    See previous revision of this answer for Swift 2 renditions.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Dec 19 '18 at 18:32

























    answered Apr 29 '16 at 6:29









    RobRob

    303k49565735




    303k49565735







    • 2





      Did you set the File Owner of the NIB to be your view controller class? If so, when you select the button, the view controller should be one of the two classes that are available under the "Automatic" setting in the assistant editor.

      – Rob
      Apr 29 '16 at 13:00






    • 1





      I'd suggest you examine your view hierarchy (eg using the view debugger) and just see what the button's superview is. Did you, for example, put the button within a container view of the CustomHeader?

      – Rob
      Apr 29 '16 at 14:19






    • 2





      I think it is worth mentioning: add 'self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension' and 'self.tableView.estimatedSectionHeaderHeight = 70' to 'viewDidLoad'. If your constraints are pining each subview on all sides, you get Self-Sized headers

      – Torsten B
      Aug 5 '16 at 13:31







    • 1





      Thank you for detailed answer! ;)

      – Tom Calmon
      Aug 23 '16 at 19:34






    • 1





      To restate what @Rob said in an above comment, ensure that when you hook your IBOutlet to your xib that you select the name of the custom class, not File Owner.

      – Jacob Davis
      Feb 4 '18 at 21:09













    • 2





      Did you set the File Owner of the NIB to be your view controller class? If so, when you select the button, the view controller should be one of the two classes that are available under the "Automatic" setting in the assistant editor.

      – Rob
      Apr 29 '16 at 13:00






    • 1





      I'd suggest you examine your view hierarchy (eg using the view debugger) and just see what the button's superview is. Did you, for example, put the button within a container view of the CustomHeader?

      – Rob
      Apr 29 '16 at 14:19






    • 2





      I think it is worth mentioning: add 'self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension' and 'self.tableView.estimatedSectionHeaderHeight = 70' to 'viewDidLoad'. If your constraints are pining each subview on all sides, you get Self-Sized headers

      – Torsten B
      Aug 5 '16 at 13:31







    • 1





      Thank you for detailed answer! ;)

      – Tom Calmon
      Aug 23 '16 at 19:34






    • 1





      To restate what @Rob said in an above comment, ensure that when you hook your IBOutlet to your xib that you select the name of the custom class, not File Owner.

      – Jacob Davis
      Feb 4 '18 at 21:09








    2




    2





    Did you set the File Owner of the NIB to be your view controller class? If so, when you select the button, the view controller should be one of the two classes that are available under the "Automatic" setting in the assistant editor.

    – Rob
    Apr 29 '16 at 13:00





    Did you set the File Owner of the NIB to be your view controller class? If so, when you select the button, the view controller should be one of the two classes that are available under the "Automatic" setting in the assistant editor.

    – Rob
    Apr 29 '16 at 13:00




    1




    1





    I'd suggest you examine your view hierarchy (eg using the view debugger) and just see what the button's superview is. Did you, for example, put the button within a container view of the CustomHeader?

    – Rob
    Apr 29 '16 at 14:19





    I'd suggest you examine your view hierarchy (eg using the view debugger) and just see what the button's superview is. Did you, for example, put the button within a container view of the CustomHeader?

    – Rob
    Apr 29 '16 at 14:19




    2




    2





    I think it is worth mentioning: add 'self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension' and 'self.tableView.estimatedSectionHeaderHeight = 70' to 'viewDidLoad'. If your constraints are pining each subview on all sides, you get Self-Sized headers

    – Torsten B
    Aug 5 '16 at 13:31






    I think it is worth mentioning: add 'self.tableView.sectionHeaderHeight = UITableViewAutomaticDimension' and 'self.tableView.estimatedSectionHeaderHeight = 70' to 'viewDidLoad'. If your constraints are pining each subview on all sides, you get Self-Sized headers

    – Torsten B
    Aug 5 '16 at 13:31





    1




    1





    Thank you for detailed answer! ;)

    – Tom Calmon
    Aug 23 '16 at 19:34





    Thank you for detailed answer! ;)

    – Tom Calmon
    Aug 23 '16 at 19:34




    1




    1





    To restate what @Rob said in an above comment, ensure that when you hook your IBOutlet to your xib that you select the name of the custom class, not File Owner.

    – Jacob Davis
    Feb 4 '18 at 21:09






    To restate what @Rob said in an above comment, ensure that when you hook your IBOutlet to your xib that you select the name of the custom class, not File Owner.

    – Jacob Davis
    Feb 4 '18 at 21:09














    24














    Rob's answer, though it sounds convincing and has withstood the test of time, is wrong and always was. It's difficult to stand alone against the overwhelming crowd "wisdom" of acceptance and numerous upvotes, but I'll try to summon the courage to tell the truth.



    The problem, quite simply, is that you cannot magically turn a UIView in a nib into a UITableViewHeaderFooterView merely by declaring it so in the Identity inspector. A UITableViewHeaderFooterView has important features that are key to its correct operation, and a plain UIView, no matter how you may cast it, lacks them.




    • A UITableViewHeaderFooterView has a contentView, and all your custom subviews must be added to this, not to the UITableViewHeaderFooterView.



      But a UIView mysteriously cast as a UITableViewHeaderFooterView lacks this contentView in the nib. Thus, when Rob says "Add your controls and constraints in IB", he is having you add subviews directly to the UITableViewHeaderFooterView, and not to its contentView. The header thus ends up incorrectly configured.




    • Another sign of the issue is that you are not permitted to give a UITableViewHeaderFooterView a background color. If you do, you'll get this message in the console:




      Setting the background color on UITableViewHeaderFooterView has been deprecated. Please set a custom UIView with your desired background color to the backgroundView property instead.




      But in the nib, you cannot help setting a background color on your UITableViewHeaderFooterView, and you do get that message in the console.



    So what's the right answer to the question? There's no possible answer. Apple has made a huge goof here. They have provided a method that allows you to register a nib as the source of your UITableViewHeaderFooterView, but there is no UITableViewHeaderFooterView in the Object Library. Therefore this method is useless. It is impossible to design a UITableViewHeaderFooterView correctly in a nib.



    This is a huge bug in Xcode. I filed a bug report on this matter in 2013 and it is still sitting there, open. I refile the bug year after year, and Apple keeps pushing back, saying "It has not been determined how or when the issue will be resolved." So they acknowledge the bug, but they do nothing about it.



    What you can do, however, is design a normal UIView in the nib, and then, in code (in your implementation of viewForHeaderInSection), load the view manually from the nib and stuff it into the contentView of your header view.



    For example, let's say we want to design our header in the nib, and we have a label in the header to which we want to connect an outlet lab. Then we need both a custom header class and a custom view class:



    class MyHeaderView : UITableViewHeaderFooterView 
    weak var content : MyHeaderViewContent!

    class MyHeaderViewContent : UIView
    @IBOutlet weak var lab : UILabel!



    We register our header view's class, not the nib:



    self.tableView.register(MyHeaderView.self,
    forHeaderFooterViewReuseIdentifier: self.headerID)


    In the view xib file, we declare our view to be a MyHeaderViewContent — not a MyHeaderView.



    In viewForHeaderInSection, we pluck the view out of the nib, stuff it into the contentView of the header, and configure the reference to it:



    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



    It's dreadful and annoying, but it is the best you can do.






    share|improve this answer

























    • Why do you need MyHeaderView at all? Instead of subclassing UITableViewHeaderFooterView (which isn't recommended by Apple - "You can use this class as-is without subclassing in most cases.") you can register it directly - tableView.register(UITableViewHeaderFooterView.self, ...). Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.

      – Piotr
      Jun 16 '18 at 20:40












    • Great answer btw. Good job on trying to understand Apple's mess :)

      – Piotr
      Jun 16 '18 at 20:42











    • @Piotr “Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.” And crash? Why would that be good?

      – matt
      Jun 16 '18 at 21:03











    • Also “You can use this class as-is without subclassing in most cases.” is not a recommendation against subclassing. I subclass it just fine.

      – matt
      Jun 16 '18 at 21:05











    • contentView.subviews[0] as! will work

      – Piotr
      Jun 16 '18 at 21:24















    24














    Rob's answer, though it sounds convincing and has withstood the test of time, is wrong and always was. It's difficult to stand alone against the overwhelming crowd "wisdom" of acceptance and numerous upvotes, but I'll try to summon the courage to tell the truth.



    The problem, quite simply, is that you cannot magically turn a UIView in a nib into a UITableViewHeaderFooterView merely by declaring it so in the Identity inspector. A UITableViewHeaderFooterView has important features that are key to its correct operation, and a plain UIView, no matter how you may cast it, lacks them.




    • A UITableViewHeaderFooterView has a contentView, and all your custom subviews must be added to this, not to the UITableViewHeaderFooterView.



      But a UIView mysteriously cast as a UITableViewHeaderFooterView lacks this contentView in the nib. Thus, when Rob says "Add your controls and constraints in IB", he is having you add subviews directly to the UITableViewHeaderFooterView, and not to its contentView. The header thus ends up incorrectly configured.




    • Another sign of the issue is that you are not permitted to give a UITableViewHeaderFooterView a background color. If you do, you'll get this message in the console:




      Setting the background color on UITableViewHeaderFooterView has been deprecated. Please set a custom UIView with your desired background color to the backgroundView property instead.




      But in the nib, you cannot help setting a background color on your UITableViewHeaderFooterView, and you do get that message in the console.



    So what's the right answer to the question? There's no possible answer. Apple has made a huge goof here. They have provided a method that allows you to register a nib as the source of your UITableViewHeaderFooterView, but there is no UITableViewHeaderFooterView in the Object Library. Therefore this method is useless. It is impossible to design a UITableViewHeaderFooterView correctly in a nib.



    This is a huge bug in Xcode. I filed a bug report on this matter in 2013 and it is still sitting there, open. I refile the bug year after year, and Apple keeps pushing back, saying "It has not been determined how or when the issue will be resolved." So they acknowledge the bug, but they do nothing about it.



    What you can do, however, is design a normal UIView in the nib, and then, in code (in your implementation of viewForHeaderInSection), load the view manually from the nib and stuff it into the contentView of your header view.



    For example, let's say we want to design our header in the nib, and we have a label in the header to which we want to connect an outlet lab. Then we need both a custom header class and a custom view class:



    class MyHeaderView : UITableViewHeaderFooterView 
    weak var content : MyHeaderViewContent!

    class MyHeaderViewContent : UIView
    @IBOutlet weak var lab : UILabel!



    We register our header view's class, not the nib:



    self.tableView.register(MyHeaderView.self,
    forHeaderFooterViewReuseIdentifier: self.headerID)


    In the view xib file, we declare our view to be a MyHeaderViewContent — not a MyHeaderView.



    In viewForHeaderInSection, we pluck the view out of the nib, stuff it into the contentView of the header, and configure the reference to it:



    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



    It's dreadful and annoying, but it is the best you can do.






    share|improve this answer

























    • Why do you need MyHeaderView at all? Instead of subclassing UITableViewHeaderFooterView (which isn't recommended by Apple - "You can use this class as-is without subclassing in most cases.") you can register it directly - tableView.register(UITableViewHeaderFooterView.self, ...). Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.

      – Piotr
      Jun 16 '18 at 20:40












    • Great answer btw. Good job on trying to understand Apple's mess :)

      – Piotr
      Jun 16 '18 at 20:42











    • @Piotr “Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.” And crash? Why would that be good?

      – matt
      Jun 16 '18 at 21:03











    • Also “You can use this class as-is without subclassing in most cases.” is not a recommendation against subclassing. I subclass it just fine.

      – matt
      Jun 16 '18 at 21:05











    • contentView.subviews[0] as! will work

      – Piotr
      Jun 16 '18 at 21:24













    24












    24








    24







    Rob's answer, though it sounds convincing and has withstood the test of time, is wrong and always was. It's difficult to stand alone against the overwhelming crowd "wisdom" of acceptance and numerous upvotes, but I'll try to summon the courage to tell the truth.



    The problem, quite simply, is that you cannot magically turn a UIView in a nib into a UITableViewHeaderFooterView merely by declaring it so in the Identity inspector. A UITableViewHeaderFooterView has important features that are key to its correct operation, and a plain UIView, no matter how you may cast it, lacks them.




    • A UITableViewHeaderFooterView has a contentView, and all your custom subviews must be added to this, not to the UITableViewHeaderFooterView.



      But a UIView mysteriously cast as a UITableViewHeaderFooterView lacks this contentView in the nib. Thus, when Rob says "Add your controls and constraints in IB", he is having you add subviews directly to the UITableViewHeaderFooterView, and not to its contentView. The header thus ends up incorrectly configured.




    • Another sign of the issue is that you are not permitted to give a UITableViewHeaderFooterView a background color. If you do, you'll get this message in the console:




      Setting the background color on UITableViewHeaderFooterView has been deprecated. Please set a custom UIView with your desired background color to the backgroundView property instead.




      But in the nib, you cannot help setting a background color on your UITableViewHeaderFooterView, and you do get that message in the console.



    So what's the right answer to the question? There's no possible answer. Apple has made a huge goof here. They have provided a method that allows you to register a nib as the source of your UITableViewHeaderFooterView, but there is no UITableViewHeaderFooterView in the Object Library. Therefore this method is useless. It is impossible to design a UITableViewHeaderFooterView correctly in a nib.



    This is a huge bug in Xcode. I filed a bug report on this matter in 2013 and it is still sitting there, open. I refile the bug year after year, and Apple keeps pushing back, saying "It has not been determined how or when the issue will be resolved." So they acknowledge the bug, but they do nothing about it.



    What you can do, however, is design a normal UIView in the nib, and then, in code (in your implementation of viewForHeaderInSection), load the view manually from the nib and stuff it into the contentView of your header view.



    For example, let's say we want to design our header in the nib, and we have a label in the header to which we want to connect an outlet lab. Then we need both a custom header class and a custom view class:



    class MyHeaderView : UITableViewHeaderFooterView 
    weak var content : MyHeaderViewContent!

    class MyHeaderViewContent : UIView
    @IBOutlet weak var lab : UILabel!



    We register our header view's class, not the nib:



    self.tableView.register(MyHeaderView.self,
    forHeaderFooterViewReuseIdentifier: self.headerID)


    In the view xib file, we declare our view to be a MyHeaderViewContent — not a MyHeaderView.



    In viewForHeaderInSection, we pluck the view out of the nib, stuff it into the contentView of the header, and configure the reference to it:



    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



    It's dreadful and annoying, but it is the best you can do.






    share|improve this answer















    Rob's answer, though it sounds convincing and has withstood the test of time, is wrong and always was. It's difficult to stand alone against the overwhelming crowd "wisdom" of acceptance and numerous upvotes, but I'll try to summon the courage to tell the truth.



    The problem, quite simply, is that you cannot magically turn a UIView in a nib into a UITableViewHeaderFooterView merely by declaring it so in the Identity inspector. A UITableViewHeaderFooterView has important features that are key to its correct operation, and a plain UIView, no matter how you may cast it, lacks them.




    • A UITableViewHeaderFooterView has a contentView, and all your custom subviews must be added to this, not to the UITableViewHeaderFooterView.



      But a UIView mysteriously cast as a UITableViewHeaderFooterView lacks this contentView in the nib. Thus, when Rob says "Add your controls and constraints in IB", he is having you add subviews directly to the UITableViewHeaderFooterView, and not to its contentView. The header thus ends up incorrectly configured.




    • Another sign of the issue is that you are not permitted to give a UITableViewHeaderFooterView a background color. If you do, you'll get this message in the console:




      Setting the background color on UITableViewHeaderFooterView has been deprecated. Please set a custom UIView with your desired background color to the backgroundView property instead.




      But in the nib, you cannot help setting a background color on your UITableViewHeaderFooterView, and you do get that message in the console.



    So what's the right answer to the question? There's no possible answer. Apple has made a huge goof here. They have provided a method that allows you to register a nib as the source of your UITableViewHeaderFooterView, but there is no UITableViewHeaderFooterView in the Object Library. Therefore this method is useless. It is impossible to design a UITableViewHeaderFooterView correctly in a nib.



    This is a huge bug in Xcode. I filed a bug report on this matter in 2013 and it is still sitting there, open. I refile the bug year after year, and Apple keeps pushing back, saying "It has not been determined how or when the issue will be resolved." So they acknowledge the bug, but they do nothing about it.



    What you can do, however, is design a normal UIView in the nib, and then, in code (in your implementation of viewForHeaderInSection), load the view manually from the nib and stuff it into the contentView of your header view.



    For example, let's say we want to design our header in the nib, and we have a label in the header to which we want to connect an outlet lab. Then we need both a custom header class and a custom view class:



    class MyHeaderView : UITableViewHeaderFooterView 
    weak var content : MyHeaderViewContent!

    class MyHeaderViewContent : UIView
    @IBOutlet weak var lab : UILabel!



    We register our header view's class, not the nib:



    self.tableView.register(MyHeaderView.self,
    forHeaderFooterViewReuseIdentifier: self.headerID)


    In the view xib file, we declare our view to be a MyHeaderViewContent — not a MyHeaderView.



    In viewForHeaderInSection, we pluck the view out of the nib, stuff it into the contentView of the header, and configure the reference to it:



    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



    It's dreadful and annoying, but it is the best you can do.







    share|improve this answer














    share|improve this answer



    share|improve this answer








    edited Apr 29 '18 at 13:31

























    answered Apr 29 '18 at 13:22









    mattmatt

    331k46536737




    331k46536737












    • Why do you need MyHeaderView at all? Instead of subclassing UITableViewHeaderFooterView (which isn't recommended by Apple - "You can use this class as-is without subclassing in most cases.") you can register it directly - tableView.register(UITableViewHeaderFooterView.self, ...). Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.

      – Piotr
      Jun 16 '18 at 20:40












    • Great answer btw. Good job on trying to understand Apple's mess :)

      – Piotr
      Jun 16 '18 at 20:42











    • @Piotr “Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.” And crash? Why would that be good?

      – matt
      Jun 16 '18 at 21:03











    • Also “You can use this class as-is without subclassing in most cases.” is not a recommendation against subclassing. I subclass it just fine.

      – matt
      Jun 16 '18 at 21:05











    • contentView.subviews[0] as! will work

      – Piotr
      Jun 16 '18 at 21:24

















    • Why do you need MyHeaderView at all? Instead of subclassing UITableViewHeaderFooterView (which isn't recommended by Apple - "You can use this class as-is without subclassing in most cases.") you can register it directly - tableView.register(UITableViewHeaderFooterView.self, ...). Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.

      – Piotr
      Jun 16 '18 at 20:40












    • Great answer btw. Good job on trying to understand Apple's mess :)

      – Piotr
      Jun 16 '18 at 20:42











    • @Piotr “Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.” And crash? Why would that be good?

      – matt
      Jun 16 '18 at 21:03











    • Also “You can use this class as-is without subclassing in most cases.” is not a recommendation against subclassing. I subclass it just fine.

      – matt
      Jun 16 '18 at 21:05











    • contentView.subviews[0] as! will work

      – Piotr
      Jun 16 '18 at 21:24
















    Why do you need MyHeaderView at all? Instead of subclassing UITableViewHeaderFooterView (which isn't recommended by Apple - "You can use this class as-is without subclassing in most cases.") you can register it directly - tableView.register(UITableViewHeaderFooterView.self, ...). Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.

    – Piotr
    Jun 16 '18 at 20:40






    Why do you need MyHeaderView at all? Instead of subclassing UITableViewHeaderFooterView (which isn't recommended by Apple - "You can use this class as-is without subclassing in most cases.") you can register it directly - tableView.register(UITableViewHeaderFooterView.self, ...). Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.

    – Piotr
    Jun 16 '18 at 20:40














    Great answer btw. Good job on trying to understand Apple's mess :)

    – Piotr
    Jun 16 '18 at 20:42





    Great answer btw. Good job on trying to understand Apple's mess :)

    – Piotr
    Jun 16 '18 at 20:42













    @Piotr “Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.” And crash? Why would that be good?

    – matt
    Jun 16 '18 at 21:03





    @Piotr “Then if you want to access your MyHeaderViewContent you can use contentView as! MyHeaderViewContent.” And crash? Why would that be good?

    – matt
    Jun 16 '18 at 21:03













    Also “You can use this class as-is without subclassing in most cases.” is not a recommendation against subclassing. I subclass it just fine.

    – matt
    Jun 16 '18 at 21:05





    Also “You can use this class as-is without subclassing in most cases.” is not a recommendation against subclassing. I subclass it just fine.

    – matt
    Jun 16 '18 at 21:05













    contentView.subviews[0] as! will work

    – Piotr
    Jun 16 '18 at 21:24





    contentView.subviews[0] as! will work

    – Piotr
    Jun 16 '18 at 21:24











    0














    I don't have enough reputation to add comment to Matt answer.



    Anyway, the only thing missing here is to remove all subviews from UITableViewHeaderFooterView.contentView before adding new views. This will reset reused cell to initial state and avoid memory leak.






    share|improve this answer



























      0














      I don't have enough reputation to add comment to Matt answer.



      Anyway, the only thing missing here is to remove all subviews from UITableViewHeaderFooterView.contentView before adding new views. This will reset reused cell to initial state and avoid memory leak.






      share|improve this answer

























        0












        0








        0







        I don't have enough reputation to add comment to Matt answer.



        Anyway, the only thing missing here is to remove all subviews from UITableViewHeaderFooterView.contentView before adding new views. This will reset reused cell to initial state and avoid memory leak.






        share|improve this answer













        I don't have enough reputation to add comment to Matt answer.



        Anyway, the only thing missing here is to remove all subviews from UITableViewHeaderFooterView.contentView before adding new views. This will reset reused cell to initial state and avoid memory leak.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 15 '18 at 20:29









        copiedcopied

        4116




        4116





















            -1














            Rob's answers seems right but Matt has provided a great details how Rob’s answer is wrong. While on the other hand Apple used Rob’s method in there example.
            Follow this link.



            https://developer.apple.com/library/archive/samplecode/TableViewUpdates/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010139-Intro-DontLinkElementID_2






            share|improve this answer























            • - Hello down voter, please give the reason.

              – AsifHabib
              Feb 26 at 18:47















            -1














            Rob's answers seems right but Matt has provided a great details how Rob’s answer is wrong. While on the other hand Apple used Rob’s method in there example.
            Follow this link.



            https://developer.apple.com/library/archive/samplecode/TableViewUpdates/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010139-Intro-DontLinkElementID_2






            share|improve this answer























            • - Hello down voter, please give the reason.

              – AsifHabib
              Feb 26 at 18:47













            -1












            -1








            -1







            Rob's answers seems right but Matt has provided a great details how Rob’s answer is wrong. While on the other hand Apple used Rob’s method in there example.
            Follow this link.



            https://developer.apple.com/library/archive/samplecode/TableViewUpdates/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010139-Intro-DontLinkElementID_2






            share|improve this answer













            Rob's answers seems right but Matt has provided a great details how Rob’s answer is wrong. While on the other hand Apple used Rob’s method in there example.
            Follow this link.



            https://developer.apple.com/library/archive/samplecode/TableViewUpdates/Introduction/Intro.html#//apple_ref/doc/uid/DTS40010139-Intro-DontLinkElementID_2







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Dec 7 '18 at 16:31









            AsifHabibAsifHabib

            758818




            758818












            • - Hello down voter, please give the reason.

              – AsifHabib
              Feb 26 at 18:47

















            • - Hello down voter, please give the reason.

              – AsifHabib
              Feb 26 at 18:47
















            - Hello down voter, please give the reason.

            – AsifHabib
            Feb 26 at 18:47





            - Hello down voter, please give the reason.

            – AsifHabib
            Feb 26 at 18:47

















            draft saved

            draft discarded
















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid


            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.

            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f36926612%2fswift-how-creating-custom-viewforheaderinsection-using-a-xib-file%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            Top Tejano songwriter Luis Silva dead of heart attack at 64

            ReactJS Fetched API data displays live - need Data displayed static

            政党