Add tableview as subview (if you have to inherit from UITableVC) - iphone

What I want is a tableview with an ad view on top for admob and pull to refresh. Right now i'm using the tableheaderview, but that scrolls and I need the ad to persist. viewforheader doesn't scroll, but gets rid of the standard headers that i need for the section headers. if im inheriting from a tableVC, is there anyway to override loadview to build a static view to hold an ad and then have the tableview below that? i've tried writing loadview and can get the ad, but when trying the tableview, i get "unable to restore selected frame" in the console. i'm using culver's pull to refresh technique as its very simple to implement. i know a tableVC assumes the root view is a tableview so how can i get around that? every solution on the net says use a standard uiviewcontroller, but im stuck cause of the pull to refresh
this is in my loadview:
self.tableView = [[UITableView alloc] initWithFrame:CGRectMake(0, 60, 320, 400) style:UITableViewStylePlain];
self.tableView.dataSource = self;
self.tableView.delegate = self;
[self.view addSubview:self.tableView];

Make your view controller inherit from UIViewController, but continue to implement <UITableViewDataSource, UITableViewDelegate>. Add your TableView as a sub-view of the UIViewController's built-in view, pointing to File's Owner for data source and delegate. Make the table view less than 100% of the height, and have the ad as another view within the main view that makes it appear below/above the table.
source: I do this in my apps, and they are on the store.

There are lots of things you could try.
You could stop inheriting from UITableViewController, like everyone says. If you're using Culver's PullRefreshTableViewController, adapt it to just subclass UIViewController. You'll need to add back the tableView property, and adopt the UITableViewDataSource and UITableViewDelegate protocols if you do.
You could set the contentInset of the table view to leave room at the top, and define scrollViewDidScroll: in your controller to reposition the ad view appropriately on each scroll. (UITableView subclasses UIScrollView, so it will call the UIScrollViewDelegate methods if you define them.)

Related

How to move an entire UITableView down programmatically

I have a tableview that I need to push down and resize smaller. I cannot use the content inset for this as its not the content I need to push down. I need to actually move the entire tableview down 44px.
What I have tried:
CGRect tableViewFrame = self.tableView.frame;
tableViewFrame.origin.y = 44;
self.tableView.frame = tableViewFrame;
Thank you in advance!
You need to change your UITableViewController to a regular UIViewController and then add the table as a subview. Then, just manually implement the UITableViewDelegate and UITableViewDataSource protocols in that view controller. After doing this, you can move the table view with your above code like any other subview.
Have a look at this answer for an example.
Convert your UITableviewController to a UIViewController and then add the tableview as subview in your nib. Also don't just change the y coordinate of tableview. instead define a new frame for your tableview e.g. self.tableView.frame = CGRectMake(x,y,width,height)
you can not move UITableviewController down, instead of this you can do like this
Create a file having UIViewController subclass with XIB.(create a seperate view controller)
Add UITableView as subview
Then wired up the UITableview , delegate and datasource.
Implement delegate protocols for tableview
use your code as it is ..
it works fine.. :)

Add Subview to UITableView's Parent view

I'm trying to add a subview to the parent view of a UITableView in order to have a subview that does not move when the UITableView is scrolled.
When I add the subview to the UITableView it moves with the table on user scrolling action.
So far, my scenario is that I have 3 UINavigationControllers on a UITabBarController.
Each has a UITableViewController.
I would like to add a subview to the screen of the individual controllers. However, I don't want the new subview to be added to the entire application like this:
[[[UIApplication sharedApplication] keyWindow] addSubview:newView];
When I push a new controller to the stack or pop one, I would like the subview to move and disappear with the UITableView it's on top of. Keeping the fact that when the user scrolls the UITableView, the new subview sits at its position without moving.
So that the UITableView and the new subview are on the same level.
Thanks
A couple of approaches:
First, to add a view to a table view that stays when the user scrolls, if your table only has one section, you can add a header for that section. For example, this adds a plain blue UIView as a header to the tableview:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.bounds.size.width, 50)];
view.backgroundColor = [UIColor blueColor];
return view;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
return 50;
}
Second, if you have more than one section, though, that trick doesn't work, and you'll want to use a UIViewController instead of a UITableViewController, add you header to the top of that controller's view and add a tableview to the bottom of that view. You'll want to make sure you set up (a) an IBOutlet for the tableview (generally to a property called tableView, to avoid confusion); and (b) specify the view controller as this tableview's data source and delegate.
Third, if you want this header to appear on every scene in your app, another approach (in iOS5+ apps) is to use view controller containment, create a container view that has your header, and then add your app's first scene as a child view of that view container. It's not hard, but if you're a new developer, I might discourage you from trying that. I've done it and it works well, but it's not for all people.
Fourth, if your app is using a navigation controller, you could customize the navigation bar to render your custom look and feel. It all depends upon what you're trying to accomplish with the header.
Add your subview to the tableview..
Implement the delegate method scrollViewDidScroll.
On scrollViewDidScroll, just set the required center of the subview you want.

Adding UIView subview to single table view controller in navigation stack

I'm working on an app that has three table view controllers in a navigation stack. The root view controller and the second VC have toolbars, but I want to add a subview to the second view controller like this. (The color is just there for visualization.)
I want to add the view programmatically, since I haven't been able to do it with IB without major headaches. Right now, I've been able to kind of get what I want by drawing a UIView in the second view controller like this:
- (void)viewDidLoad {
[super viewDidLoad]
UIView *detailView = [[UIView alloc] initWithFrame:CGRectMake(0, 392, 320, 44)];
detailView = [UIColor redColor];
[self.navigationController.view addSubview:detailView];
[detailView release];
}
The problem with this approach is that once the UIView is loaded in the second view controller, it stays loaded and is drawn in the third and root view controllers. I've tried a variety of methods of removing the UIView, including setting the detailView to nil in viewDidUnload, calling removeFromSuperview in didSelectRowAtIndexPath (which removed the view from the whole stack).
I've also tried adding the subview to self.view, but that pushes it below the visible area of the table view, so I have to scroll up to see it, and it snaps back down when I let go.
Clearly, adding this subview to the navigation controller is not the best way to do what I want, but I'm at a loss as to where to go from here.
As you've already discovered, you definitely should not be reaching up into the navigation controller's view.
You want your SecondViewController to be an UIViewController that implements the UITableViewDelegate and UITableViewDataSource and whose view lays out the UITableView and the UIView you wish to use for your stationary 'footer' in it's own main UIView.
It helps to keep in mind that UITableViewController is ultimately is just a convenience for creating a view controller whose view consists entirely of a UITableView.
Anyway, rather than attempt to put a pile of that code inline in this answer, you can browse it (or svn co) from this read-only svn repo.
EDITED (now that it's not midnight, putting some code/explanation directly in answer):
For the controller to be pushed onto the nav stack that needs the footer create a new UIViewController-based class (do NOT check the 'UITableViewController subclass' box in the template selection dialog).
Add instance variables for the UITableView and the UIView that is to be the extra bottom view.
#interface SecondViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
UITableView* tableView;
UIView* customFooterView;
}
#property (nonatomic,retain) IBOutlet UITableView* tableView;
#property (nonatomic,retain) IBOutlet UIView* customFooterView;
#end
In IB add a UITableView and UIView to the existing root view for the controller and lay them out as desired (probably worth altering the auto-resize parameters too if your app can be used in both landscape and portrait). Hook up the two views to the outlets defined for them in the "File's Owner" and also ensure you hook up the UITableView's delegate and dataSource properties to point at the "File's Owner."
Then just implement the UITableViewDelegate and UITableViewDataSource protocols as appropriate for your application.
If you want to lay out the entire 'footer' view in IB then go right ahead. Otherwise you can easily add items programmatically in viewDidLoad (and remember to tear it down in viewDidUnload).
I don't like the approach. You should put your table view inside another view, and put your detail view together in that view.
Despite of that, I think you can remove your view in viewWillDisappear method of your view controller. I also notice that you did not keep your detailView as a private variable, which you should do because you need to reference it when removing it later (I still wonder how you have done it.)
Note that viewDidUnload is called in case of view unloading (i.e. releasing from its controller), so it is not related to navigation.
Not sure which behavior you're looking for but try one of these:
Assign the detailView to the tableFooterView property of the tableview on the second VC.
Reduce the height of the table view and add the detailView to self.view.

How do I reduce the height of a TableView when it is constructed in IB?

I wanted to add a view to the bottom of my screen. The controller is a UITableViewController, how do I shrink the tableView and add a extra view at the bottom of the tableview?
I've tried setting the frame of self.tableView in different places (viewDidLoad, viewWillAppear etc) but nothing happens. The tableView is created by IB and not programtically.
I've tried added a footer to my table view but that's not what I want, because the footer actually scrolls up, I want a static non moving View at the bottom of the screen.
I'm not saying you can't do it otherwise, but you may not want a UITableViewController for this situation. You can still have your view controller implement UITableViewDelegate and UITableViewDataSource, but place a vanilla UIView in your nib, into which you place a UITableView. Then just make sure to set the view outlet to the UIView containing your table. This has the effect of allowing you to create your additional view within IB. I just tried this and it appeared to work.
I'm guessing you're using a UINavigationController. When you push a controller onto your navigation stack, UINavigationController resizes its view to full screen, ignoring the geometry and autoresizing behavior you've defined in IB.
This resizing seems to happen after viewWillAppear:. In the past I've had some success resizing a table view and adding a sibling view in viewDidAppear:, after calling [super viewDidAppear:]. This is a bit risky though, since Apple could break it by changing how UINavigationController works behind the scenes.
A safer option is to push a view controller onto your navigation stack that controls a wrapper view. Then add your UITableView and its sibling as subviews of that wrapper view. The annoying thing about this option is that you'll probably want to use a nested UITableViewController to manage your non-full screen table view, but the documentation for UIViewController says it's designed to manage full screen views only. If you decide to ignore this admonition and nest your view controllers anyway, you'll find that viewWill/DidAppear/Disappear don't get called on the nested controller, so you'll have to manually delegate those methods from your wrapper view controller. This lack of support for nested controllers is one of my biggest pet peeves about UIKit, and I've gone to great lengths to engineer around it.
If you want to toe the line and use view controllers only for full screen views, you can push a normal view controller that controls your full screen wrapper view, manually implement all the UITableViewDataSource and UITableViewDelegate methods in your view controller, and set it as the delegate for your table view.
you want to change the -loadView method. Not viewDidLoad or viewWillAppear. This will allow you to make additional configurations with your tableview even if it is created in IB.
- (void)loadView {
[super loadView];
CGRect titleRect = CGRectMake(0, 0, 300, 40);
UILabel *tableTitle = [[UILabel alloc] initWithFrame:titleRect];
tableTitle.textColor = [UIColor blueColor];
tableTitle.backgroundColor = [self.tableView backgroundColor];
tableTitle.opaque = YES;
tableTitle.font = [UIFont boldSystemFontOfSize:18];
tableTitle.text = [curTrail objectForKey:#"Name"];
self.tableView.tableHeaderView = tableTitle;
[self.tableView reloadData];
[tableTitle release];
}
I don't know how to do it in IB but the way to do it in code is with this:
- (void) loadView
{
UITableView *tv = [[UITableView alloc] initWithFrame: rect
style: UITableViewStyleGrouped];
// finishg configuring table view
self.view = tv;
[tv release];
}
Trying to do it in two stages -- style first and then frame or frame first and then style -- neither of them works.

superview and parentviewcontroller nil after adding a subview

I think I'm missing something fundamental and so I want to ask the community for some help. I'm building an app based around a basic iPhone Utility Application. My MainView and FlipsideView share some elements so I have created separate ViewControllers and nib files for those pieces. In order to do this I have done the following:
1. Created a viewcontroller called searchDateViewController which is the file's owner of searchDateView.xib
2. searchDateView.xib is basically a UIView with a UILabel inside, the view is wired up correctly
3. Inside both MainViewController.m and FlipsideViewController.m I add a subview as folllows:
- (void)loadView{
[super loadView];
searchDateViewController = [[SearchDateViewController alloc] initWithNibName:#"SearchDateView" bundle:nil];
[[searchDateViewController view] setFrame:[searchDateView frame]];
[[self view] addSubview:[searchDateViewController view]];
...
}
Everything displays and works just fine. Basically depending on actions that happen in each of the main and flipside views the UILabel of the nib is changed. However, I wanted to do something slightly different if the searchDateViewController is loaded from the MainView or the FlipsideView. However, I can't seem to figure out which ViewController is adding the searchDateViewController subview.
In searchDateViewController I tried:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"superview %#", self.view.superview);
NSLog(#"parentviewcontroller %#", self.parentViewController);
}
In both cases I get nil.
So my question is - can I find out which ViewController is adding searchDateViewController a a subview? If so how? Or if my logic here is completely messed up, how should I be doing this?
Thanks!
viewDidLoad is invoked when the view controller has loaded its view. In your case, that happends in this line:
[[searchDateViewController view] setFrame:[searchDateView frame]];
At that moment, you haven't yet called addSubview: so it is no wonder the view's superview is nil.
To solve your problem, you should define a property inside SearchDateViewController to distinguish between the different cases. This property would then be set accordingly by the parent controller that creates the SearchDateViewController instance.
Generally, I do not think it is a good idea to use a UIViewController subclass as a controller for a view that is used as a subview of one or several fullscreen views rather than be used as a fullscreen view itself. Much of UIViewController's logic works on the assumption that it is used to manage a fullscreen view. For instance, with your design, I think it's possible that SearchDateViewController will modify the view's frame when the device orientation changes etc. Since you don't need all this functionality for a non-fullscreen subview, I suggest you subclass your SearchDateViewController directly from NSObject.
ViewController and views are completely separate.
In most cases, when you add a subview to a parent view you don't add its controller to the parent's viewController. The exception to this rule is the navigation controller which adds the controller instead of the view to maintain a hierarchy of view controllers.
Your SearchDate viewController can't find a parent controller because you never assigned one and the system does not do it automatically. You can just assign a parent controller when you evoke the view from another controller.
searchDateViewController.parentController=self;