UITableView as a tableHeaderView for another UITableView - iphone

I get stuck on the following: I want to use a single cell of a UITableView as a header view for another UITableView. The second table view is in turn an object that has been inherited from a different UITableView class, since it shares it's functionality completely. The only difference in fact is that the second UITableView has a header view (which should be the single celled UITableView).
The interface for the UITableView with the header looks like:
#import <UIKit/UIKit.h>
#import "ScheduleDetailsController.h"
#interface TodayDetailsViewController : ScheduleDetailsController <UITableViewDelegate, UITableViewDataSource> {
UITableView *headerView;
}
#property (nonatomic, retain) UITableView *headerView;
#end
And the implementation like:
#import "TodayDetailsViewController.h"
#implementation TodayDetailsViewController
#synthesize headerView;
- (void)loadView {
[super loadView];
self.headerView = [[UITableView alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
self.headerView.backgroundColor = [UIColor clearColor];
self.headerView.opaque = NO;
self.headerView.delegate = self;
self.headerView.dataSource = self;
// This is a UITableView defined as a property in the parent class
self.scheduleDetailsTable.tableHeaderView = self.headerView;
}
...
#pragma mark -
#pragma mark Table View Data Source Methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (tableView == self.scheduleDetailsTable.tableHeaderView)
return 1;
else
return [self.scheduleDetailsTable numberOfSections];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.scheduleDetailsTable.tableHeaderView)
return 1;
else
return [self.scheduleDetailsTable numberOfRowsInSection:section];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.scheduleDetailsTable.tableHeaderView) {
...
return cell;
} else
return [self.scheduleDetailsTable cellForRowAtIndexPath:indexPath];
}
#end
I get no errors when I compile and run this, but then I also don't see anything, i.e. the view stays empty. I have also tried it with replacing the header view by a UIView and then everything works like expected (i.e. the table view in the parent class is implemented correctly). I'm probably just missing something obvious, but I just can't figure it out at the moment. If someone could spot the error or could give some advice I would be very thankful.

Not for nothing, but do you really need to create a whole UITableViewController and UITableView just to use a UITableViewCell as a header? A UITableViewCell is a subclass of UIView, after all.
This works, more or less, (had to make the labels not opaque myself; I guess the UITableView normally handles that):
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
UITableViewController *testTableViewController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
UITableViewCell *tableViewCell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:NULL];
tableViewCell.textLabel.text = #"Test";
tableViewCell.textLabel.opaque = NO;
tableViewCell.detailTextLabel.text = #"123";
tableViewCell.detailTextLabel.opaque = NO;
testTableViewController.tableView.tableHeaderView = tableViewCell;
[tableViewCell release];
// self.window is initilized and hooked up in MainMenu.xib
self.window.rootViewController = testTableViewController;
[self.window makeKeyAndVisible];
[testTableViewController release];
return YES;
}
For that matter, if the above wont meet your needs why not just bump everything down in your table view by one section, and use the very top section as the "header":
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return ([super numberOfSectionsInTableView:tableView] + 1);
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section == 0) {
return 1;
} else {
return [super tableView:tableView numberOfRowsInSection:(section + 1)];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0) {
// ***** Build and return your header cell *****
} else {
NSIndexPath *newIndexPath = [NSIndexPath indexPathForRow:indexPath.row inSection:(indexPath.section + 1)];
return [super tableView:tableView cellForRowAtIndexPath:newIndexPath];
}
}
Repeat as necessary for all the other data source delegates.
Just a thought...

Well, I solved the problem by replacing the return statements of the delegate methods in my original posting from
return [self.scheduleDetailsTable cellForRowAtIndexPath:indexPath];
to
return [super tableView:tableView cellForRowAtIndexPath:indexPath];
And similarly for the other delegate methods. Now everything works like expected. Thanks to peterjb for using this syntax in his response to my original question.

Related

TableView wont display provided data

I am having a strange issue with TableView. Here is some code to clarify everything:
I am using tabbed panes and this right here is just one of my two ViewControllers:
As you can see, all I do is setting up an UIView and placing a tableView right ontop of it.
#import "SuchenCtrl.h"
#interface SuchenCtrl ()
#end
#implementation SuchenCtrl
#synthesize MainView;
#synthesize Standort;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.view = self.MainView;
[self.Standort.tableView setDelegate:self.Standort];
[self.Standort.tableView setDataSource:self.Standort];
[self.MainView addSubview:self.Standort.tableView];
}
- (id)init {
self = [super init];
if(self) {
CGRect rect = [[UIScreen mainScreen] applicationFrame];
self.MainView = [[UIView alloc] initWithFrame:rect];
[self.MainView setBackgroundColor:[UIColor blackColor]];
//Alloc UITableViewController
self.Standort = [[UIStandort alloc] initWithStyle:UITableViewStylePlain];
[self.Standort release];
[self.MainView release];
}
return self;
}
#end
Now UIStandort is another class that simply inherits from UITableViewController. It implements all necessary functions to display data. All im trying to achieve is having the table display "Test" a couple of times but it wont. The table gets displayed nicely but just with empty cells. When I breakpoint one of the implemented functions in the class UIStandort nothing gets called, so my assumption is that the delegate and sourceData are pointing to wrong classes? Though, I explicitly assign those before putting the table on the view.
UIStandort* Standort is declared with properties retain and nonatomic if this helps.
#import "UIStandort.h"
#interface UIStandort ()
#end
#implementation UIStandort
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 0;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath(NSIndexPath *)indexPath
{
UITableViewCell* cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cell.textLabel.text = #"Test";
[cell autorelease];
return cell;
}
#end
Thanks.
You return 0 for the number of row in section. The documentation states:
The number of sections in tableView. The default value is 1.
Can you try:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}

iPhone Multiple UITableView controls on one View

Im having a little issue with using more than 1 UITableView on a view.
Here's what I've done so far (using examples, etc from here and other places):
Created a class for each table. Each class is pretty basic:
.h:
#interface ConstructionDocumentsJobTable : UITableViewController <UITableViewDataSource, UITableViewDelegate> {
NSMutableArray *tableItems;
IBOutlet UITableView *itemsTable;
NSInteger recordSelected;
id <JobTableSelectionDelegate> tableSelectDelegate;
}
#property (nonatomic, retain) NSMutableArray *tableItems;
#property (nonatomic, assign) id <JobTableSelectionDelegate> tableSelectDelegate;
#end
.m:
#implementation ConstructionDocumentsJobTable
#synthesize tableItems, tableSelectDelegate;
#pragma mark -
#pragma mark View Life Cycle
-(void) loadView
{
}
-(void) dealloc
{
[tableItems release];
[super dealloc];
}
#pragma mark -
#pragma mark Table view data source
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [tableItems count];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [tableItems objectAtIndex:indexPath.row];
return cell;
}
#pragma mark -
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//pass the tap value back to the delegate
}
Both are completely identical, save the names.
When Im making the call to the first one, it is called in the ViewDidLoad method of the controller of the view. It's pretty basic:
NSMutableArray *tableItems = [[NSMutableArray alloc] initWithCapacity:intMax];
//strDocumentType is set elsewhere and loaded here
if(strDocumentType == #"typea"){
[tableItems addObject:#"Type A - Value 1"];
[tableItems addObject:#"Type A - Value 2"];
}
else {
[tableItems addObject:#"Type B - Value 1"];
}
if(jobTableController == nil){
jobTableController = [[ConstructionDocumentsJobTable alloc] init];
[jobTableController loadView];
jobTableController.tableItems = tableItems;
jobTableController.tableSelectDelegate = self;
}
[tableJobList setDataSource:jobTableController];
[tableJobList setDelegate:jobTableController];
jobTableController.view = jobTableController.tableView;
The second table is built when a cell in the first table is selected. So, in the first tables selection method, the delegate is called back from the parent controller, which then has this:
NSMutableArray *tableTypeItems = [[NSMutableArray alloc] initWithCapacity:100];
if(tableSelect == #"plumbing"){
[tableTypeItems addObject:#"Something"];
[tableTypeItems addObject:#"Other"];
}
if(typeTableController == nil){
typeTableController = [[ConstructionDocumentsTypeTable alloc] init];
[typeTableController loadView];
typeTableController.tableItems = tableTypeItems;
typeTableController.tableSelectDelegate = self;
}
[tableTypeList setDataSource:typeTableController];
[tableTypeList setDelegate:typeTableController];
typeTableController.view = typeTableController.tableView;
[typeTableController.tableView reloadData];
//Code to move the first table off the screen and move this one into view goes here
Ive been stuck on this for days, and I really need to get this done!!!
Im sure it's something REALLLLLLY simple.
Any help you guys can pass along would be HUGELY appreciated.
Thanks everyone.
Your only defining itemsTable in your header, try defining another table, and in your cellForRowAtIndexPath try this:
if(itemsTable1){
/* stuff for first table */
} else {
/* stuff for second table */
}
And do the same for each UITableVIew Delegate

interesting UITableView Datasource behavior

So i have this very basic ipad view controller and i was doing some testing with multiple UITableViews in the same view. The issue I was having was when I selected a row, it would throw a EXC_BAD_ACCESS so I turned on the stack logging and found this
*** -[VCTables tableView:didSelectRowAtIndexPath:]: message sent to deallocated instance 0x4c0ad40
Then i started looking at my code and could not figure out for the life of me what is causing it. I have UITableViewDataSource and UITableViewDelegate on my view controller and have all the appropriate code to handle did select row at index. It draws the tables properly, it just doesnt seem to be hitting self as the datasource.
I have tried building the UITableViews in code, and also in Interface builder. I have re-downloaded and installed XCode 3.2.3 SDK 4.0.2 after uninstalling it and restarting. I couldn't for the life of me see what I am missing but after doing the previous, I am convinced now (i guess) that it is a code issue rather than the IDE, I just cant open my eyes wide enough to see the code issue.
Also, this happens with just one table as well. And with 2 tables, it happens no matter which table I select
here is some code:
VCTables.h
#import <UIKit/UIKit.h>
#interface VCTables : UIViewController<UITableViewDelegate,UITableViewDataSource> {
UITableView *table1;
UITableView *table2;
}
#end
VCTables.m
#import "VCTables.h"
#implementation VCTables
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 50;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 7;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *CellIdentifier = [[NSString alloc] initWithString:#"yourCell"];
if(tableView.tag == 2000){
[CellIdentifier release];
CellIdentifier = [[NSString alloc] initWithString:#"myCell"];
}
UITableViewCell *cell = (UITableViewCell*)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
if(tableView.tag == 2000){
[cell.textLabel setText:[NSString stringWithFormat:#"%d",indexPath.row]];
}else{
[cell.textLabel setText:[NSString stringWithFormat:#"%d%d",indexPath.row,indexPath.section]];
}
[CellIdentifier release];
return cell;
}
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
return NO;
}
- (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return NO;
}
-(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
NSLog(#"selected");
}
- (void)viewDidLoad {
[super viewDidLoad];
table1 = [[UITableView alloc] initWithFrame:CGRectMake(20, 73, 320, 480) style:UITableViewStylePlain];
table1.delegate=self;
table1.dataSource=self;
table1.tag=2000;
[self.view addSubview:table1];
table2 = [[UITableView alloc] initWithFrame:CGRectMake(483, 73, 320, 480) style:UITableViewStylePlain];
table2.delegate=self;
table2.dataSource=self;
table2.tag=2000;
[self.view addSubview:table2];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)viewDidUnload {
[super viewDidUnload];
}
- (void)dealloc {
[super dealloc];
}
#end
I'm not sure it gets more basic than that. Please tell me the rookie mistake that is so painfully obvious that I must stop posting here. Thanks in advance
message sent to deallocated instance 0x4c0ad40
This error indicates that your VCTables instance has been released. The UITableView still has a reference to it (the delegate property), so it is trying to send a message to a released object. The common term for this is a zombie.
In order to debug, you should look at how the memory management is being done for your VCTables object. Where is it created, and who owns it?
If you can make use of Apple's performance tools, try using the zombies tool to find out where that VCTables object is released.

having two table views in a single xib one of plain style and the other grouped styled

I want to use two tableviews under the segmentedcontrol. one is plain styled and the other is groupstyled. How can i controll the delegate and datasource for the two table views?
--
Regards,
Syed yusuf
HI,
I have to inserted the data into table view by using sqlite.But now i need that data in to two table views in next view..I have arranged segmented bar.Am getting two table views but the values in r not displaying.It is displaying NULL.
Using multiple or two exclusive table in the save UI view controller sharing the same data source.
I face a problem like this, if anybody needs later...
I try to set my ViewController as a datasource for two different table. But it did not work. 2 tables are exclusively shown at loadtime of the view. Either one will be hidden in viewDidLoad depending on a flag. Seems once dataSource is called for one table, it's not called for the 2nd table. All connections are set in IB.
The solution is to set the dataSource in code in viewDidLoad. Then it works.
-(void) viewDidLoad(){
table1.dataSource = self;
table2.dataSource = self;
if(someCondition == YES)
table1.visible = NO;
else
table2.visible = NO;
}
Since you can't change the UITableViewStyle of a UITableView once created (it can only be set at construction time), you have to have two different instances. You can do this in very different ways, but I've done it this way: add a UISegmentedControl to your interface, and set its target to the RootViewController class instance in your application. The RootViewController class could look like this:
#class DataSource;
#interface RootViewController : UITableViewController
{
#private
UITableView *_originalTableView;
UITableView *_secondaryTableView;
DataSource *_dataSource;
BOOL _showingSecondaryTableView;
}
- (IBAction)swap:(id)sender;
#end
And this might be the implementation:
#import "RootViewController.h"
#import "DataSource.h"
#implementation RootViewController
- (void)dealloc
{
[_dataSource release];
[_originalTableView release];
[_secondaryTableView release];
[super dealloc];
}
- (void)viewDidLoad
{
[super viewDidLoad];
_dataSource = [[DataSource alloc] init];
_secondaryTableView = [[UITableView alloc] initWithFrame:self.tableView.frame
style:UITableViewStyleGrouped];
_secondaryTableView.delegate = _dataSource;
_secondaryTableView.dataSource = _dataSource;
_originalTableView = [self.tableView retain];
_showingSecondaryTableView = NO;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#pragma mark -
#pragma mark IBAction method
- (IBAction)swap:(id)sender
{
if (_showingSecondaryTableView)
{
self.tableView = _originalTableView;
_showingSecondaryTableView = NO;
}
else
{
self.tableView = _secondaryTableView;
_showingSecondaryTableView = YES;
}
}
#pragma mark -
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return 5;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [NSString stringWithFormat:#"RootViewController cell %d", indexPath.row];
return cell;
}
#end
This is the interface of the DataSource class:
#import <UIKit/UIKit.h>
#interface DataSource : NSObject <UITableViewDelegate,
UITableViewDataSource>
{
}
#end
And the implementation:
#import "DataSource.h"
#implementation DataSource
#pragma mark -
#pragma mark Table view methods
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section
{
return 3;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"DataSourceCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [NSString stringWithFormat:#"DataSource cell %d", indexPath.row];
return cell;
}
#end
You can change the datasource and delegate of the UITableView instances to anything you want, at any time during runtime, which might help you encapsulate different data sources with separate controllers.
Hope this helps!

Reusable TableView header views

For performance sake it is usual to reuse UITableView' cells.
Is there a way to do the same thing with TableView header' views?
I am talking about the ones that are returned with delegate's method:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
I tried to do the following which doesn't seem to be working as expected:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
static NSString *CellIdentifier = #"Header";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier: CellIdentifier];
if (cell == nil) {
cell = [self getHeaderContentView: CellIdentifier];
}
return cell;
}
Is there a way to reuse header' views?
The reason Apple built in the ability to reuse tableview cells is because while the tableview may have many rows, only a handful are displayed on screen. Instead of allocating memory for each cell, applications can reuse already existing cells and reconfigure them as necessary.
First off, header views are just UIViews, and while UITableViewCell is a subclass of UIView, they are not intended to be placed as the view of a section header.
Further, since you generally will have far fewer section headers than total rows, there's little reason to build a reusability mechanism and in fact Apple has not implemented one for generic UIViews.
Note that if you are just setting a label to the header, you can use -(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section instead.
For something more custom, such as a label with red text (or a button, image, etc), you can do something like this:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *headerView = [[[UIView alloc] initWithFrame:CGRectMake(0,0, 320, 44)] autorelease];
UILabel *label = [[[UILabel alloc] initWithFrame:headerView.frame] autorelease];
label.textColor = [UIColor redColor];
label.text = [NSString stringWithFormat:#"Section %i", section];
[headerView addSubview:label];
return headerView;
}
You can implement that by creating UITableViewHeaderFooterView class
it is subclass of UIView
You also need to create an individual XIB as it will not be created automatically with UITableViewHeaderFooterView creation.
Register Nib with tableview
[self.tblCart registerNib:[UINib nibWithNibName:#"CartHeaderView" bundle:nil] forHeaderFooterViewReuseIdentifier:#"CartHeader"];
Now You can Access that in viewForHeaderInSection
-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
CartHeaderView *sectionHeader=[tableView dequeueReusableHeaderFooterViewWithIdentifier:#"CartHeader"];
return sectionHeader;
}
Note :
To set background color you will need to create a subview with same frame as section header and set color for that view.
you can follow
Changing the background color on a UITableViewHeaderFooterView loaded from a xib says to use contentView.backgroundColor instead
A simple yet effective solution:
#interface SectionedTableViewController ()
#property (nonatomic, strong) UINib* sectionHeaderNib;
#property (nonatomic, strong) NSMutableArray* sectionHeaders;
#end
#implementation SectionedTableViewController
#synthesize sectionHeaderNib = sectionHeaderNib_;
#synthesize sectionHeaders = sectionHeaders_;
- (void) viewDidUnload
{
self.sectionHeaders = nil;
[super viewDidUnload];
}
- (NSMutableArray*) sectionHeaders
{
if (!sectionHeaders_)
self.sectionHeaders = [NSMutableArray array];
return sectionHeaders_;
}
- (UINib*) sectionHeaderNib
{
if (!sectionHeaderNib_)
self.sectionHeaderNib = [UINib nibWithNibName: NSStringFromClass(YourHeaderView.class) bundle: nil];
return sectionHeaderNib_;
}
- (YourHeaderView*) dequeueHeader
{
return [self.sectionHeaders firstObjectPassingTest: ^(id obj) { return (BOOL) ([obj superview] == nil); }];
}
- (NSString*) sectionHeaderTitleForSection: (NSInteger) section
{
return nil;
}
- (UIView*) tableView: (UITableView*) tableView viewForHeaderInSection: (NSInteger) section
{
YourHeaderView* headerView = [self dequeueHeader];
if (!headerView)
{
headerView = [YourHeaderView instanceFromNib: self.sectionHeaderNib];
[self.sectionHeaders addObject: headerView];
}
return headerView;
}
#end