17

I've got a UITableView that I'd like to stick a 44px subview on top of. I tried tableViewHeader, but that scrolls with the rest of the table.

I tried searching and have found many people saying I need to add a UIView superview and then add my header and the UITableView to it. However I can't find an example on exactly how to do this. I tried making a new UIView subclass and laying out the subviews in IB, but I ran into trouble getting the table controller to link w/ the UITable (because I don't know enough about IB).

How can I do this with XIBs? Can someone provide an example?

Thanks for any help you guys can provide.

DOOManiac
  • 6,066
  • 8
  • 44
  • 67
  • Possible duplicate of [UITableView with fixed section headers](http://stackoverflow.com/questions/17582818/uitableview-with-fixed-section-headers) – Iulian Onofrei Oct 04 '16 at 12:50

5 Answers5

34

I finally figured this out right after posting. Figures. :)

Here's what I did, in case others run into the same problem:

  1. Delete the existing UITableViewController and its XIB. They're junk. Get really mad while you do.

  2. Make a new UIViewController subclass with a XIB

  3. Open XIB in IB and add your header stuff and a UITableView to the UIView

  4. In the IB Outlets for UITableView make sure you connect Delegate and DataSource to your File Owner

  5. In the header for your view controller, be sure to add <UITableViewDelegate, UITableViewDataSource> to implement these protocols

  6. Implement all the regular UITableView delegate and data source methods you know and love, but in your UIViewController instead of the way you're used to doing it through UITableViewController

After this things should work.

John Topley
  • 113,588
  • 46
  • 195
  • 237
DOOManiac
  • 6,066
  • 8
  • 44
  • 67
  • 2
    This should be the accepted answer. Overriding existing methods like the currently accepted answer does should be a last resort. – Paul de Lange May 11 '12 at 07:05
  • 11
    However, this does not solve the problem if you need it to be a UITableViewController for other reasons. – Kudit Jan 31 '13 at 00:38
  • @PauldeLange do you mean the answer from Steve? what is wrong with overriding existing methods? – Dejell Feb 17 '13 at 21:26
  • 3
    The problem with using a vanilla UIViewController is that you lose the ability to use UIRefreshControl, which is only supported with a UITableViewController. – Alex MDC Jan 08 '14 at 10:41
9

The problem is, UITableViewController's view property is the same thing as the tableView property. I had the same problem, wanting to put some fixed content above the table. I didn't want to change the base class, as it provides lots of great functionality I didn't want to lose or disrupt.

The fix is actually easy. The trick is to create custom set and get for self.tableView property. Then, in loadView, you replace the view with a fresh UIView and add the tableView to it. Then you're free to add subviews around the tableView. Here's how it's done:

In header:

@interface CustomTableViewController : UITableViewController
{
    UITableView *tableView;
} 

In .m:

- (UITableView*)tableView
{
    return tableView;
}

- (void)setTableView:(UITableView *)newTableView
{
    if ( newTableView != tableView )
    {
        [tableView release];
        tableView = [newTableView retain];
    }        
}

- (void)loadView {
    [super loadView];
    //save current tableview, then replace view with a regular uiview
    self.tableView = (UITableView*)self.view;
    UIView *replacementView = [[UIView alloc] initWithFrame:self.tableView.frame];
    self.view = replacementView;
    [replacementView release];
    [self.view addSubview:self.tableView];    

    //code below adds some custom stuff above the table
    UIView *customHeader = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 20)];
    customHeader.backgroundColor = [UIColor redColor];
    [self.view addSubview:customHeader];
    [customHeader release];

    self.tableView.frame = CGRectMake(0, customHeader.frame.size.height, self.view.frame.size.width, self.view.frame.size.height - customHeader.frame.size.height);
}

Enjoy!

Steve Potter
  • 1,899
  • 2
  • 22
  • 25
  • 1
    what do you say about overwriting existing methods = do you recommend it? – Dejell Feb 19 '13 at 07:42
  • 1
    Even if this works, it's technically wrong, since the docs explicitly say not to call `super` in loadView (which needs to be done for this solution to work). – lobianco Mar 28 '14 at 20:52
1

As an option it is possible to embed UITableViewController as part of UI into another UIViewController with 'Container View' element (pick one in Interface Builder from the Object library (where all other views are) ).

This way you can use UITableViewController like ordinary view (in terms of positioning) and compose any layout you need without overwritting existing table view code

EDIT:

to further expand my answer, here are the steps to accomplish the described approach:

  1. Open you storyboar in interface builder
  2. Drag'n'drop a new ViewController element to the storyboard from Object Library to add a new controller.
  3. As a child view, drag'n'drop Container View from Object Library and place it anywhere inside the ViewController

  4. Container View creates another view controller and "embedded" segue as a connection. It's save to delete this viewcontroller and to connect the Container View with the required view controller (as per the questions it's UITableViewController)

  5. To connect Container View with UITableViewController just select the container view and control+drag to the UITableViewController - select "Embed" in the popup menu

Now the controller will display inside the Container View with respect to the container's position and boundaries.

It's possible to get a link to the embeeded view controller - the system will fire "prepareForSegue" method of the host viewcontroller (created on the step 1 above) and the controller is in segue.destinationViewController property - one can customize it as required. Just make sure to set an identifier to the "embedded" segue in interface builder - this is the same process just like for any other segues.

vir us
  • 9,920
  • 6
  • 57
  • 66
1

Replace the TableViewController with a ViewController, inside it add a UIView with fixed height to place the fixed content you need, and below that add a UITableView.

Create an outlet to your TableView

 @IBOutlet weak var tableView: UITableView!

You can reuse all the funcs you already have removing the override word for example

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return data.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let c = tableView.dequeueReusableCellWithIdentifier(cellId) as UITableViewCell!
    c.textLabel?.text = "A title"
    c.detailTextLabel?.text = "A subtitle"
    return c
}

See this answer to add the automatic refresh control if you need it Pull to refresh UITableView without UITableViewController

Community
  • 1
  • 1
iyepes
  • 125
  • 1
  • 9
  • 3
    If you feel that this question is a duplicate, it should be marked as such. Don't post links to other StackOverflow posts as an answer. – JAL Oct 28 '15 at 14:35
  • The question is not duplicated, someone above said that one of the reasons not to implement the accepted answer is because they can't use the automatic refresh control, I'm showing a way to use it with the accepted answer. I would have done a comment in the accepted answer if stackoverflow allows me to do so, but I can't because it asks for 50 points of reputation. – iyepes Oct 28 '15 at 16:47
0

Define a custom UIView in storyboard or xib, have a IBOutlet reference for that UIView in View Controller. In -(void)viewWillAppear:(BOOL)animated method write [self.tableView.superview addSubview:filterHeaderView];, here filterHeaderView is the IBOutlet reference for my header view which I want to add as fixed header in my tableview.

AskIOS
  • 918
  • 7
  • 19