开发者

How to add a UIView above the current UITableViewController

开发者 https://www.devze.com 2023-02-03 20:40 出处:网络
I have difficulty adding a subview (UIView) from within the viewDidLoad method of a 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. Whe开发者_Go百科n 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.


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.


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.


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


Swift 2018

Here is my way with storyboard:

1) Add a view in storyboard.

How to add a UIView above the current UITableViewController

2) Link it with UITableViewController class:

@IBOutlet weak var copyrightLabel: UILabel!

3) Add it in code

self.navigationController?.view.addSubview(copyrightView)
copyrightView.frame = CGRect(x: 0,
                            y: self.view.bounds.size.height - copyrightView.bounds.size.height,
                            width: self.view.bounds.size.width,
                            height: copyrightView.bounds.size.height)

4) Voilla!

How to add a UIView above the current UITableViewController

The view will not scroll with the table view. It can be easy designable from the storyboard.

NOTE: This solution adds subview to the navigation controller and if you are going to another screen from here further down the nav, you will find this subview to persist, remove it using copyrightView.removeFromSuperView on viewDidDisappear while performing segue.


You can increase the zPosition of the layer of your view.

This will make it display above the other views (which have a zPosition equal to 0, by default)

self.progView.layer.zPosition++;
[self.view addSubview:self.progView];


As UITableViewController is a subclass of UIViewController, you need to add your desired view to its superview.

Swift:
self.view.superview?.addSubview(viewTobeAdded)

Objective C:
[self.view.superview addSubview: viewTobeAdded];


The problem is that the view property of UITableViewController is identical to the tableView property. What this means is that the root view is always a table view controller, and anything added as a subview will be subject to the table view functionality. This has other undesirable side effects, like your subviews scrolling when you may not want them to.

There are a couple options here. You could override loadView and install your own view and table view:

// note: untested
- (void)loadView {
    self.view = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
    self.view.backgroundColor = [UIColor whiteColor];

    UITableView *tblView = [[UITableView alloc]
        initWithFrame:CGRectZero
        style:UITableViewStylePlain
    ];

    tblView.autoresizingMask =
        UIViewAutoresizingFlexibleWidth |
        UIViewAutoresizingFlexibleHeight
    ;

    self.tableView = tblView;
    [self.view addSubview:tblView];
    [tblView release];
}

And then when you need to add a subview, add it below or above self.tableView as appropriate.

Another option is just to create a UIViewController subclass that does what you need. UITableViewController honestly doesn't add that much, and the little functionality it does implement is easily replicated. There are articles like Recreating UITableViewController to increase code reuse that explain how to do this pretty easily.


I had similar problem and got it solved using below code :

     [self.navigationController.view insertSubview:<subview> 
       belowSubview:self.navigationController.navigationBar];

This inserts view in correct place using controllers present in Stack.


The Apple example "iPhoneCoreDataRecipes" is using a NIB to load a header onto a UITableView. See here:


I added a image above the the table. I used this code:

self.tableView.tableHeaderView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"header.png"]];


Solution for swift (equivalent to Daniel Saidi):

If your controller is a UITableViewController in a Storyboard or XIB and you wish to reassign self.view to a standard UIView while preserving your existing table view:

@IBOutlet var tableViewReference: UITableView!
var viewReference: UIView!

Then in your implementation file:

Add these instance variables to your table view controller file:

override var tableView: UITableView! {
    get { return tableViewReference }
    set { super.tableView = newValue }
}

override var view: UIView! {
    get { return viewReference }
    set { super.view = newValue }
}

override func viewDidLoad() {
    super.viewDidLoad()
    self.edgesForExtendedLayout = UIRectEdge.None
    self.extendedLayoutIncludesOpaqueBars = false
    self.automaticallyAdjustsScrollViewInsets = false

    //rewiring views due to add tableView as subview to superview
    viewReference = UIView.init(frame: tableViewReference.frame)
    viewReference.backgroundColor = tableViewReference.backgroundColor
    viewReference.autoresizingMask = tableViewReference.autoresizingMask
    viewReference.addSubview(tableViewReference)
}

In your Storyboard or XIB file: Connect the tableView in the UITableViewController to the tableViewReference variable.

Then you will be able to add child views as follows:

self.view.addSubView(someView)


Update for iOS 11:

It is now possible to add an Subview to UITableView when using AutoLayout constraints to the safe area. These Views will not scroll along the TableView.

This example places a view below the NavigationBar on top of the UITableView of a UITableViewController

[self.tableView addSubview:self.topBarView];
[NSLayoutConstraint activateConstraints:@[
    [self.topBarView.topAnchor constraintEqualToAnchor:self.tableView.safeAreaLayoutGuide.topAnchor],
    [self.topBarView.leadingAnchor constraintEqualToAnchor:self.tableView.safeAreaLayoutGuide.leadingAnchor],
    [self.topBarView.trailingAnchor constraintEqualToAnchor:self.tableView.safeAreaLayoutGuide.trailingAnchor],
    [self.topBarView.heightAnchor constraintEqualToConstant:40.0]
]];


try something like this:

[self.tableView addSubview:overlayView];
overlayView.layer.zPosition = self.tableView.backgroundView.layer.zPosition + 1;


You may simply put the following code in viewDidAppear:

[self.tableView.superview addSubview:<your header view>];


try: [self.view bringSubviewToFront:self.progView];

Or you can try to add self.progView to your table's view.


To keep UIView above table view in UITableViewController I'm using one(or more) of delegate methods (UITableViewDelegate).

override func viewDidLoad() {
    super.viewDidLoad()
    self.tableView.addSubview(headerView)
}

func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
    tableView.addSubview(overlayView) // adds view always on top
}
// if using footers in table view
tableView(_ tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int) { ... }

As views can only have one superview that seams too be good solution, correct me if I'm wrong. Still getting 60fps so it's fine for me.


Swift 4

This is the most simplified version of a number of answers here where we are recomposing the view hierarchy. This approach does not require additional outlets for storyboards / nibs and will also work with programmatically constructed instances.

class MyTableViewController: UITableViewController {

    var strongTableView: UITableView?

    override var tableView: UITableView! {
        get {
            return strongTableView ?? super.tableView
        }
        set {
            strongTableView = newValue
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // theoretically we could use self.tableView = self.tableView but compiler will not let us assign a property to itself
        self.tableView = self.view as? UITableView
        self.view = UIView(frame: self.tableView!.frame)
        self.view.addSubview(tableView)

    }

}


To add a customView above the current UITableViewController, it must be a nice way to use 'self.navigationController.view addSubview:customView' like Daniel commented.

However, in case of implementing customView that serves as navigationBar, Daniel's way can cause unexpected result to default or custom navigationBar on other navigationViewControllers that is in front and back of the UITableViewController.

The best simple way is just converting UITableViewController into UIViewController which has no limit on layout it's subviews. But, if you're struggling with massive, long legacy UITableViewController code, the story is totally different. We don't have any sec for converting.

In this case, you can simply highjack tableView of UITableViewController and solve this whole problem.

The most important thing we should know is UITableViewController's 'self.view.superview' is nil, and 'self.view' is UITableView itself.

First, highjack the UITableVIew.

UITableView *tableView = self.tableView;

Then, replace 'self.view'(which is now UITableView) with a new UIView so that we can layout customViews with no-limitation.

UIView *newView = UIView.new;
newView.frame = tableView.frame;
self.view = newView;

Then, put UITableView we highjacked before on the new self.view.

[newView addSubview:tableView];
tableView.translatesAutoresizingMaskIntoConstraints = NO;
[tableView.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
[tableView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
[tableView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
[tableView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;

Now, we can do whatever we want on this brand new fancy 'self.view' on UITableViewController.

Bring a custom View, and just add as subView.

UIView *myNaviBar = UIView.new;
[myNaviBar setBackgroundColor:UIColor.cyanColor];
[self.view addSubview:myNaviBar];

myNaviBar.translatesAutoresizingMaskIntoConstraints = NO;
[myNaviBar.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
[myNaviBar.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor].active = YES;
[myNaviBar.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor].active = YES;
[myNaviBar.heightAnchor constraintEqualToConstant:90].active = YES;

gif


There may be reasons not to do this, but this works for me so far. If you use an ap

Inside viewDidLayoutSubviews you can run this, but make sure to only run it once obviously

self.searchTableView = [[UITableView alloc] initWithFrame:self.tableView.frame style:UITableViewStylePlain];
self.searchTableView.backgroundColor = [UIColor purpleColor];
[self.view.superview addSubview:self.searchTableView];


I had a similar problem where I wanted to add a loading indicator on top of my UITableViewController. To solve this, I added my UIView as a subview of the window. That solved the problem. This is how I did it.

-(void)viewDidLoad{
    [super viewDidLoad];
    //get the app delegate
    XYAppDelegate *delegate = [[UIApplication sharedApplication] delegate];

    //define the position of the rect based on the screen bounds
    CGRect loadingViewRect = CGRectMake(self.view.bounds.size.width/2, self.view.bounds.size.height/2, 50, 50);
    //create the custom view. The custom view is a property of the VIewController
    self.loadingView = [[XYLoadingView alloc] initWithFrame:loadingViewRect];
    //use the delegate's window object to add the custom view on top of the view controller
    [delegate.window addSubview: loadingView]; 
}


This worked for me:

self.view.superview?.addSubview(yourCustomView)
self.view.bringSubviewToFront(yourCustomView)
0

精彩评论

暂无评论...
验证码 换一张
取 消