How to add a UIView above the current UITableViewController

I have difficulty adding a subview (UIView) from within the viewDidLoad method of a UITableViewController

This works:

[self.view addSubview:self.progView];

But you can see the table cell lines bleed through the UIView progView.

I've tried this approach:

[self.view.superview insertSubview:self.progView aboveSubview:self.view];

Which is an attempt to add the progView, UIView to the superview, above the current view. When I try this, the UIView never appears.

-- UPDATE --

Following is the latest attempt:

UIView *myProgView = (UIView *)self.progView; //progView is a method that returns a UIView

[self.tableView insertSubview:myProgView aboveSubview:self.tableView]; 
[self.tableView bringSubviewToFront:myProgView];

Result is the same as [self.view addSubview:self.progView]; The UIView appears but seemingly behind the Table.


Solution 1:

I tried the approach above, but did not get it to work. I also found it to require too much configuration and code, since it requires setting up the table view from scratch (something that is easily done from within the storyboard).

Instead, I added the view that I wanted to add above my UITableView into the UITableViewController's UINavigationController's view, as such:

[self.navigationController.view addSubview:<view to add above the table view>];

This approach requires that you have embedded the UITableViewController in a UINavigationController, but even if you do not want a navigation controller, you can still use this approach and just hide the navigation bar.

Solution 2:

So 7 years have passed since my original answer, and I happen to stumble upon this problem again. Let's solve this properly once and for all:

  1. In viewDidLoad, add your subview to the (table) view.
  2. Pin the constraints relative to the safe area layout guide. This stops it from scrolling with the table contents (as pointed out in cornr's answer).
  3. In viewDidLayoutSubviews, bring the subview to the front. This ensures it doesn't get lost behind the table separators.

Swift:

override func viewDidLoad() {
    super.viewDidLoad()

    // 1.
    view.addSubview(mySubview)

    // 2. For example:
    mySubview.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate([
        mySubview.widthAnchor.constraint(equalToConstant: 100),
        mySubview.heightAnchor.constraint(equalToConstant: 100),
        mySubview.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
        mySubview.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor)
    ])
}

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    // 3.
    view.bringSubviewToFront(mySubview)
}

Objective-C:

- (void)viewDidLoad
{
    [super viewDidLoad];

    // 1.
    [self.view addSubview:self.mySubview];

    // 2.
    self.mySubview.translatesAutoresizingMaskIntoConstraints = false;
    [NSLayoutConstraint activateConstraints:@[
        [self.mySubview.widthAnchor constraintEqualToConstant:100],
        [self.mySubview.heightAnchor constraintEqualToConstant:100],
        [self.mySubview.centerXAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.centerXAnchor],
        [self.mySubview.centerYAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.centerYAnchor]
    ]];
}

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];

    // 3.
    [self.view bringSubviewToFront:self.mySubview];
}

Phew, glad that's done! Seeing how much saner this answer is, I'll omit my original answer.

Fun fact: 7 years on and I'm still an iOS developer.

Solution 3:

Ive been able to add a subview on top of a uitableviewcontroller by using uiviewcontroller containment.

UITableViewController is actually very handy when it comes to static cells and this is probably the only time where the common answer "just use uitableview" may actually not viable.

So this is how I do it.

  1. give your UITableViewController a StoryBoard identifier i.e. MyStaticTableView
  2. create a brand new UIViewController subclass and call it UITableViewControllerContainer
  3. place this controller in place of your UITableViewController inside your storyboard
  4. add a subview to the new controller and link it to an outlet called like "view_container"
  5. on you UITableViewControllerContainer viewDidLoad method

add code like:

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    UITableViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:@"MyStaticTableView"];
    [self addChildViewController:vc];
    [self.view_container addSubview:vc.view];
}

Problems you may have:

  1. if you have extra top space then be sure to add the flag "wants fullscreen" to your UITableViewController
  2. if it doesn't resize properly on your UITableViewControllerContainer

add code:

- (void)viewWillAppear:(BOOL)animated
{
    [[self.view_container.subviews lastObject] setFrame:self.view.frame];
}

at this point from your UITableViewController you can access you container view directly with

self.view.superview.superview

and whatever you add to it will be show on top your table view controller