I'm building an app for a blog site.
I have a UINavigationController with a UITableViewController as it's root view.
I laid this out in a storyboard no problem, but I'm trying to drag an iAd view to the bottom of the screen and xcode will not let me add it.
It looks like I have to switch from a subclass of UITableViewController to a subclass of UIViewController, and just put my delegate and datasource methods in my subclassed UIViewController.
This seems wrong to me. I'm just trying to end up with a UITableView of article headlines, with a navbar up top, and an iAd at the bottom...
Advice? Suggestions?
Thanks in advance.
One of the easiest ways to accomplish this is using the UITableView's tableFooterView property. Yes, I know the footer stays at the bottom of the table, but it doesn't have to. You can set its frame within the table. Add the iAd as the footer like so:
self.tableView.tableFooterView = iAd;
Then, to adjust the frame of the iAd as the table scrolls, implement the UIScrollView delegate method: (This is possible because UITableView is a subclass of UIScrollView)
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
CGRect iAdFrame = iAd.frame;
CGFloat newOriginY = table.contentOffset.y + table.frame.size.height - iAdFrame.size.height;
CGRect newIAdFrame = CGRectMake(iAdFrame.origin.x, newOriginY, iAdFrame.size.width, iAdFrame.size.height);
iAd.frame = newIAdFrame;
}
You can see that the implementation is easy enough. We simply use the contentOffset y to determine how far down the frame of the iAd should be.
I tried to use the example above by NJones with adjusting the position of the tableFooterView, but I found out it was hard to manage it when reloading the data or refreshing the table.
Then I found out that this could be done by adding the iAd banner to the superview of the tableViewController's view.
self.bannerViewController = [[BannerViewController alloc] init];
[self.bannerViewController.view setHidden:YES];
[self.bannerViewController.view setFrame:CGRectMake(0, self.view.superview.frame.size.height - self.tabBarController.tabBar.frame.size.height - 50, 320, 50)];
[self.view.superview addSubview:self.bannerViewController.view];
[self.bannerViewController loadBanner];
When the banner is loaded I create a tableFooterView to make space for the last cell in the tableViewController
-(void)bannerDidLoad{
[self.bannerViewController.view setHidden:NO];
self.tableView.tableFooterView = [[UIView alloc];
initWithFrame:self.bannerViewController.view.frame];
}
I had to make some changes to the solution posted by NJones, since there was a problem with the ad not being displayed on top of all other cells/views.
First make sure your tableViewController is a AdBannerViewDelegate:
#interface MyTableViewController () <ADBannerViewDelegate>
Adding the AdBanner to the tableviewcontroller:
- (void)viewDidLoad {
[super viewDidLoad];
...
ADBannerView *adBanner = [[ADBannerView alloc]initWithAdType:ADAdTypeBanner];
adBanner.delegate = self;
self.tableView.tableFooterView = adBanner;
}
The code to position the ad banner is taken from NJones, I only added the last line to bring the ad banner to the front:
-(void)positionAdBanner {
ADBannerView *adBanner = (ADBannerView *) self.tableView.tableFooterView;
if (adBanner) {
CGRect iAdFrame = adBanner.frame;
CGFloat newOriginY = self.tableView.contentOffset.y + self.tableView.frame.size.height - iAdFrame.size.height;
CGRect newIAdFrame = CGRectMake(iAdFrame.origin.x, newOriginY, iAdFrame.size.width, iAdFrame.size.height);
adBanner.frame = newIAdFrame;
[self.tableView bringSubviewToFront:adBanner];
}
}
This function gets called whenever the view is going to layout its subviews (so you only need it here, no need to check for scrolling, etc):
-(void)viewWillLayoutSubviews{
[super viewWillLayoutSubviews];
[self positionAdBanner];
}
You also should handle the ADBannerViewDelegate methods:
-(void)bannerViewDidLoadAd:(ADBannerView *)banner
{
banner.hidden = NO;
[self positionAdBanner];
}
-(void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
banner.hidden = YES;
}
Related
I have a UITableView in a UINavigationController that contains a rightBarButtonItem which should hide the UITableView and show an MKMapView instead. The button seems to work great: it hides the UITableView, and shows the MKMapView. However, this MKMapView is empty. As in, completely white. I've tried to use a UILabel (just for testing purposes), and that doesn't appear either, so the problem must occur when I add the MKMapView (and UILabel) to the view hierarchy. Some relevant code:
- (void)viewDidLoad {
[super viewDidLoad];
self.mapView = [[MKMapView alloc] init];
mapView.hidden = YES;
[self.view addSubview:self.mapView];
// Some other stuff, table set up, etc.
}
That is the viewDidLoad of a class that inherits from UITableViewController. Now, I use the following method that gets called when tapped on the rightBarButtonItem of the UINavigationController:
- (void) toggleView {
if (self.mapView.isHidden) {
self.mapView.hidden = NO;
self.tableView.hidden = YES;
self.viewButton.title = #"List";
}
else {
self.mapView.hidden = YES;
self.tableView.hidden = NO;
self.viewButton.title = #"Map";
}
}
I am certain that function gets called, I have checked using NSLog. Also, the UITableView correctly disappears, and, I assume, the MKMapView (or whatever other UIView object for that matter) appears, but is empty/completely white. Does anybody see why I'm not seeing maps when trying to switch to Map View?
You should give it a size and position.
CGSize size = self.view.frame.size;
self.mapView =[[MKMapView alloc] initWithFrame:CGRectMake(0,0, size.width, size.height)];
To check the size in the console, add the following line in your toogleview method:
NSLog(#"%#", self.mapView);
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.
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)
Is there any way to show a tab bar after it has been hidden?
Got a tabbar-nav structure. For one of the tabs, I need to hide the tab bar for its 2nd and 3rd level view. But at the same time I will need to show its 1st and 4th view.
The sample code from Elements isn't really applicable here I think.
I've found quite a good pragmatic solution to this problem - make the UITabBarController's view larger than it needs to be, so that the actual UITabBar is clipped by the screen.
Assuming that the tab bar view normally fills its superview, this sort of thing should work:
CGRect frame = self.tabBarController.view.superview.frame;
if (isHidden)
{
CGFloat offset = self.tabBarController.tabBar.frame.size.height;
frame.size.height += offset;
}
self.tabBarController.view.frame = frame;
The tab bar is still showing, but it's off the bottom of the screen, so appears to have been hidden.
It might have performance implications if it causes extra clipping, but so far, it seems to work.
The UIViewControllers that are pushed onto the navigation stack can do the something like the following:
- (void)viewWillAppear:(BOOL)animated {
self.tabBarController.tabBar.hidden = NO; // Or YES as desired.
}
EDIT: Added additional code below to deal with the frame. Don't think I particular recommend this idea since it relies on the internal default view structure of a UITabBarController.
Define the following category on UITabBarController:
#interface UITabBarController (Extras)
- (void)showTabBar:(BOOL)show;
#end
#implementation UITabBarController (Extras)
- (void)showTabBar:(BOOL)show {
UITabBar* tabBar = self.tabBar;
if (show != tabBar.hidden)
return;
// This relies on the fact that the content view is the first subview
// in a UITabBarController's normal view, and so is fragile in the face
// of updates to UIKit.
UIView* subview = [self.view.subviews objectAtIndex:0];
CGRect frame = subview.frame;
if (show) {
frame.size.height -= tabBar.frame.size.height;
} else {
frame.size.height += tabBar.frame.size.height;
}
subview.frame = frame;
tabBar.hidden = !show;
}
#end
Then, instead of using the tabBar.hidden change I originally suggested, do the following:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.tabBarController showTabBar:NO];
}
Obviously making sure that the implementation has included the category definition so that 'showTabBar' is known.
You need to implement a delegate method
- (BOOL)tabBarController:(UITabBarController *)tabBarController2 shouldSelectViewController:(UIViewController *)viewController
Inside that you can check which index is selected and show the tab bar
if([[tabBarController.viewControllers objectAtIndex:0] isEqual:viewController])// it is first tab
{
tabBarController.tabBar.hidden = FALSE;
}
I know this is an old post but i think the below code would help to hide the tabbar on the viewcontroller you don't want it on and has the added benefit of automatically readding the tabbar when you come back from that view controller
UIViewController *hideTabbarViewController = [[UIViewController alloc] init];
hideTabbarViewController.hidesBottomBarWhenPushed = YES;
[[self navigationController] hideTabbarViewController animated:YES];
whenever I set my tableHeaderView I'm not seeing it in the Simulator.
If I add it as a subview, it ends up getting drawn underneath the section header. Any idea what I'm missing here?
I do have a XIB file. I didn't see any properties in IB to affect headerViews though.
- (void)viewDidLoad {
[super viewDidLoad];
MyTitleView *titleView = [[MyTitleView alloc] initWithFrame:CGRectMake(60,0,260,40)];
titleView.label.text = #"My Title";
self.navigationItem.titleView = titleView;
[titleView release];
StandardTableHeaderView *headerView = [[StandardTableHeaderView alloc] initWithFrame:CGRectMake(0,0,320,44)];
self.tableView.tableHeaderView = headerView;
// [self.view addSubview:self.tableView.tableHeaderView];
// [headerView release];
NSLog(#"Header: %#",self.tableView.tableHeaderView); //Logs ] Header: <StandardTableHeaderView: 0x5a508b0; frame = (0 0; 320 44); layer = <CALayer: 0x5a51130>>
Edit:
StandardTableHeaderView.m init method:
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.backgroundColor = [UIColor redColor];
self.label = [[UILabel alloc] initWithFrame:CGRectMake(frame.origin.x,0,frame.size.width,frame.size.height)];
self.label.backgroundColor = [UIColor clearColor];
self.label.textColor = [UIColor whiteColor];
self.label.font = [UIFont fontWithName:#"Helvetica" size:16];
[self addSubview:self.label];
}
return self;
}
Note! If you expect the table view to set the frame of the header view for you you're mistaken and will get very weird results.
I had mine set to CGRectZero thinking it'd solve itself, but the thing wouldn't appear and when it did, it would appear tucked in under the navbar or sticking down into the first cell and such.
First of all your code looks fine.
2 possible problems:
self.tableView is not set
tableHeaderView is overridden after viewDidLoad
I was having the same problem and noticed that any tableViewHeader that I defined in my NIB would not appear if I initialized my tableview by invoking:
- (id)initWithStyle:(UITableViewStyle)style
But it magically appeared without effort when I initialized my tableview by using:
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
Weird.
I tried to put the setup of tableHeaderView in ViewDidLoad and loadView methods - it didn't work: part of the view was white, not visible completely. Then I tried to put it to ViewDidAppear and it worked:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self setupTableHeaderView];
}
Just came across the same problem where I had it in viewDidLoad() it worked in the simulator for iPhone 11 Pro but not for iPhone 8. Moving it to viewDidAppear() solved the problem, but I ended up moving it to viewDidLayoutSubviews() which I felt was the most appropriate place for it - works perfectly in the simulator across all iPhone models.
override func viewDidLayoutSubviews() {
let headerView = Bundle.main.loadNibNamed("HeaderView", owner: self, options: nil)?.first as? UIView
headerView!.frame = CGRect(x: 0, y: 0, width: table_view!.frame.width, height: 160.0)
tableView!.tableHeaderView = headerView
}
I had a problem where i was putting a view inside a tableviewcontroller as a header and it would not show up when i ran the app, the problem was i added the header when there were no prototype cells. if you add at least one prototype cell, then add the header above it, then delete the cell (if need be), it registered it as a headerview properly
when you UIView inside the UITableView in the storyboard and you try to set tableView.tableHeaderView = YOUR_VIEW, it won't work. You have to first drag-drop that view outside view controller create its outlet and then set
tableView.tableHeaderView = YOUR_VIEW
Drawing upon the key clue from Kalle I've added this
open var intrinsicContentheight: CGFloat
{
achievementLabel.sizeToFit()
let labelSize = achievementLabel.bounds.size
return labelSize.height + 13 + .verticalMargin
}
abomination to the class that defines the header view.
If you don't have dynamic type you'd have a simpler method yet.
Here is the usage in the view controller that sets up the tableheaderview
with the instance of this class
loanApplicationStepsView.bounds.size.height = loanApplicationStepsView.intrinsicContentheight
cause as Kalle have stated just because there is no func tableViewHeightForHeader(_ tableView: UITableView) -> CGFloat in uitableviewdelegate doesn't mean you don't have to do the dance to compute it
the width get set automatically by uikit
Try setting UITableViewDelegate in your header and place the following code in your .m file.
#pragma mark UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 44.0f;
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
StandardTableHeaderView *headerView = [[StandardTableHeaderView alloc] initWithFrame:CGRectMake(0,0,320,44)];
return headerView;
}
I added a UITableView as a subview to a custom UIView class I'm working on. However I noticed that whenever I scroll the table it calls my classes layoutSubviews. I'm pretty sure its the UIScrollview that the table is inheriting from which is actually doing this but wanted to know if there is a way to disable this functionality and if not why is it happening? I don't understand why when you scroll a scrollview it needs its superview to layout its subviews.
Code:
#implementation CustomView
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.clipsToBounds = YES;
UITableView *tableView = [[UITableView alloc] initWithFrame:CGRectMake(0.0, 15.0, 436.0, 132.0) style:UITableViewStylePlain];
tableView.dataSource = self;
tableView.delegate = self;
tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
tableView.backgroundColor = [UIColor clearColor];
tableView.showsVerticalScrollIndicator = NO;
tableView.contentInset = UIEdgeInsetsMake(kRowHeight, 0.0, kRowHeight, 0.0);
tableView.tag = componentIndex;
[self addSubview:tableView];
[tableView release];
}
return self;
}
- (void)layoutSubviews {
// This is called everytime I scroll the tableview
}
#end
Yes, a UIScrollView does call layoutsubviews whenever it scrolls. I could've sworn this was stated in the documentation somewhere, but I guess not.
Anyways, the prevailing idea for this is that a UIScrollView should layout its stuff so that views that currently can't be seen shouldn't be laid out. As users scroll in the scroll view, it should add and remove subviews as necessary. I'm guessing this is what TableViews use to enqueue table cells that get hidden.
Is there any reason why you would care if layoutsubviews gets called or not?
UITableView at least does appear to layout its superview. This behavior can be problematic when you have a layoutSubviews method that might be expensive (e.g. if you call some JavaScript).
The quick fix is add an intermediary subview that prevents the scroll view from laying out your superview. Instead, it will layout the intermediate subview.
This could be somewhat imperfect but it should work for most cases:
Assume UIView * intermediateView is defined as an instance variable:
-(id) initWithFrame:(CGRect)frame
{
self = [super initWithFrame: frame];
if (self)
{
UIScrollView * theScrollView; // = your scroll view or table view
intermediateView = [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
// Ensures your intermediate view will resize its subviews.
intermediateView.autoresizesSubviews = YES;
// Ensure when the intermediate view is resized that the scroll view
// is given identical height and width.
theScrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
[intermediateView addSubview: theScrollView];
// Ensure the frame of the scroll view is exactly the bounds of your
// intermediate view.
theScrollView.frame = bottomContainerView.bounds;
[self addSubview: intermediateView];
}
return self;
}
-(void) layoutSubviews
{
intermediateView.frame = CGRectMake(0, 50, 42, 42); // replace with your logic
}
Not sure i understand your issue correctly but when you scroll a tableview it removes the cells not shown from the memory and loads them again when they are scrolled back into visibility (cells are allocated on demand, only the visible ones) , in effect doing what you seem to be describing.