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:
- In
viewDidLoad
, add your subview to the (table) view. - 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).
- 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.
- give your UITableViewController a StoryBoard identifier i.e. MyStaticTableView
- create a brand new UIViewController subclass and call it UITableViewControllerContainer
- place this controller in place of your UITableViewController inside your storyboard
- add a subview to the new controller and link it to an outlet called like "view_container"
- 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:
- if you have extra top space then be sure to add the flag "wants fullscreen" to your UITableViewController
- 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.
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!
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)
精彩评论