How does a table view controller communicate with its detail view? - objective-c

I have a table view, and if a certain row is tapped, the detail view will show up. But how does the detail view know which row was tapped ?
So for instance, I have a MainController which displays names. If I tap "Johnny" The next screen should show a label with the string "Johnny". How would I do that ?
[EDIT - Added code]
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ArticleView *article = [self.storyboard instantiateViewControllerWithIdentifier:#"ArticleView"];
[article.myLabel setText:#"random"];
[self.navigationController pushViewController:article animated:YES];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
NSDictionary *info = [json objectAtIndex:indexPath.row];
cell.textLabel.text = [info objectForKey:#"username"];
cell.textLabel.backgroundColor = nil;
cell.textLabel.textColor = [UIColor whiteColor];
cell.detailTextLabel.backgroundColor = nil;
cell.detailTextLabel.textColor = [UIColor whiteColor];
cell.detailTextLabel.text = [info objectForKey:#"user_pic"];
// Configure the cell...
return cell;
}
ArticleView.h
#interface ArticleView : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *myLabel;
#end
ArticleView.m
-> Synthesizing properties

You could pass a object (i.e. the String Jonny) to the detail view controller as a property.
#interface ArticleViewController : UIViewController
#property (retain) ArticleView *articleView;
#end
//Don't forget to synthesize name
in the tableview controller
-(void)tableView:(UITableView *)tableview didSelectRowAtIndexPath:(NSIndexPath)indexPath
{
NSDictionary *info = [json objectAtIndex:indexPath.row];
ArticleViewController *articleViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"ArticleViewController"];
articleViewController.articleView = articleView;
[self.navigationController pushViewController: articleViewController animated:YES];
}

the table view delegate has the method tableView:didSelectRowAtIndexPath:, in this method you present your detail view so you know which row was selected from the indexPath parameter and can pass any info you need when you create the new view controller

Assuming you're talking iPhone here, I would have a property name on my detail view:
#propery (nonatomic, retain) NSString * name;
and an init method as follows:
- (id) initWithName: (NSString *) name
{
self = [super initWithNibName: #"<view-controller-nib-name>" bundle: nil];
if (self) {
self.name = name;
}
return self;
}
and then set name label in viewDidLoad
In the tableView:didSelectRowAtIndexPath: method, get the name referenced by the index path, create and init a detail view controller using initWithName:, then push on the nav stack.

Related

Null data when loading DetailView

I have UITableView I want to click on row and load detail view but I have null information in my Log in detail view
would you please help m win this implementation
Thanks in advance!
cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath
*)indexPath
{
//Load Cell for reuse
static NSString *CellIdentifier = #"TestCell";
TestCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell =[ [[NSBundle mainBundle] loadNibNamed:#"TestCell" owner:nil options:nil]
lastObject];
}
_tests = [server info];
_t = [NSMutableString stringWithFormat:#"%# ", [_tests objectAtIndex:indexPath.row]];
NSString *testdata = _t;
_components = [testdata componentsSeparatedByString:#","];
for (NSString *aComponent in _components) {
if ([aComponent hasPrefix:#"titletest"]) {
_subComponents = [aComponent componentsSeparatedByString:#":"];
_testString = [_subComponents[1] stringByTrimmingCharactersInSet:[NSCharacterSet
characterSetWithCharactersInString:#"\""]];
}if ([aComponent hasPrefix:#"note"]){
_subComponents = [aComponent componentsSeparatedByString:#":"];
_noteString = [_subComponents[1] stringByTrimmingCharactersInSet:[NSCharacterSet
characterSetWithCharactersInString:#"\""]];
break;
}
}
cell.title.text = _testString;
cell.note.text = _noteString;
return cell;
}
didSelectRowAtIndexPath method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath
*)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
TestDetailView *c = [[TestDetailView alloc] init];
///I don't know what should I put here to c
[self.navigationController pushViewController:c animated:YES];
}
but in My detail view
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:YES];
NSLog(#" test : %#",_Info.datas); ==>> my log was (null)
}
My Detail View Header
#property (strong, nonatomic) IBOutlet UILabel *bDetailTitle;
#property (strong, nonatomic) IBOutlet UILabel *bDetailNote;
#property (nonatomic, strong) NSArray *datas;
#property (nonatomic,strong) TestTable *Info;
#end
I'm assuming that TestDetailView is a ViewController subclass and that you have a property on it possibly called datas. If that is the case you appear to create your detail view controller in didSelectRowAtIndexPath: like this:
TestDetailView *c = [[TestDetailView alloc] init];
But then immediately change it the pointer to something else (possibly nil):
c = [_datas objectAtIndex:indexPath.row];
objectAtIndex returns an id which means your code probably doesn't throw a warning in Xcode, but at run time if [_datas objectAtIndex:indexPath.row] does not return a UIViewController (and I suspect that it does not) your app could crash. If it is not throwing an error or crashing you possibly have another problem in that _datas is nil in your parent view controller.
It is a very common technique to pass values to view controllers I would recommend reading up on how to do this. This type of question has been asked on S.O. many times:
How to set value for NSString in another view controller from one viewcontroller

Load information from text field to table view

Hi I am trying to make an app that has a text field, tableview and a button.
When you press the button it adds the information from the text field to de tableview.
Do any one know when I can find a tutorial for this? I had one on a book but I cant find it.
Thanks in advance.
Here is my code so far:
code of .h
#interface BlockNotasViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
IBOutlet UITableView *tableNota;}
#property (strong, nonatomic) IBOutlet UITextField *textonota;
#property (nonatomic, retain) IBOutlet UITableView *tableNota;
#property (nonatomic, strong) NSMutableArray * arrayNota;
- (IBAction)AddNota:(id)sender;
#end
code of .m
- (void)viewDidLoad{
[super viewDidLoad];
self.textonota.delegate = self;
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return NO;
}
- (IBAction)AddNota:(id)sender {
[arrayNota addObject: textonota.text];
[tableNota reloadData];
}
//TableView------------------------------------
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [arrayNota count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (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] init];
}
cell.textLabel.text = [arrayNota objectAtIndex: indexPath.row];
return cell;
}
//----------------------------------------------
#end
No more errors but the button doesnt do anything
You can set up a NSMutableArray property that contains all the cells you want to add to the tableView.
When that button is pressed, you create a cell with a tag, and set text of that cell to the text of your UITextField:
cell.textLabel.text = aUITextField.text;
then add the cell into that NSMutableArray, and call [tableView reloadData] to refresh the table view.
In cellForRowAtIndexPath:indexPath delegate, you can use that tag to determine which cell in NSMutableArray to return, for example:
[return [self.cellArray objectAtIndex:indexPath.row]];
Hope this helps! :)
You should keep mutable array with strings. And use this array for fill content of tableview as data source. In delegate method - (UITableViewCell*) tableView:cellForRowAtIndexPath: you will create UITableViewCell if needed like this:
- (UITableViewCell*) tableView: (UITableView*) tableView
cellForRowAtIndexPath: (NSIndexPath*) indexPath
{
UITableViewCell* cell = [tableViewdequeueReusableCellWithIdentifier: #"identifier"];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle: UITableViewCellStyleDefault
reuseIdentifier: #"identifier"] autorelease];
}
cell.textLabel.text = [myMutableArray objectAtIndex: indexPath.row];
return cell;
}
- (NSInteger)tableView: (UITableView*) tableView numberOfRowsInSection:(NSInteger)section
{
return [myMutableArray count];
}
- (IBAction) pressButton: (id) sender
{
[myMutableArray addObject: textField.text];
[myTableView reloadData];
}
Every book for iOS developers contains information about UITableView.
Please read https://stackoverflow.com/questions/3403049/best-book-resources-for-learning-ios-programming

UITableView in UIViewController - Connecting cells to UIViewController

I have a UITableView in a UIViewController. I have other UIViewControllers created in Storyboard that I want to connect to my cells.
How can I connect UIViewControllers created in Storyboard to my cells?
MyViewController.h
#interface MyViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
IBOutlet UITableView *myTableView;
}
#property (nonatomic, retain) UITableView *myTableView;
MyViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *array = [[NSArray alloc] initWithObjects:#"CELL1", #"CELL2 & CELL3", nil];
self.listData = array;
[array release];
[myTableView setDataSource:self];
[myTableView setDelegate:self];
[[self view] addSubview:myTableView];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [listData count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UpdatesTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray* views = [[NSBundle mainBundle] loadNibNamed:#"UpdatesTableViewCell" owner:nil options:nil];
for (UIView *view in views) {
if([view isKindOfClass:[UITableViewCell class]])
{
cell = (UpdatesTableViewCell*)view;
}
}
}
NSUInteger row = [indexPath row];
cell.textLabel.text = [listData objectAtIndex:row];
return cell;
}
You can either create segues from this controller to the other ones and then choose which segue to perform when someone taps a cell or you can use a reference to the storyboard to call instantiateViewControllerWithIdentifier: and make that controller active on a tap.

Tabbed ios application with multiple table views

I'm using XCode 4.2 and testing my build on iPad 5.0.
I started building an application using the standard Tabbed application in XCode and then added code to have 2 uitableviews inside the first tab.
It compiles, but the table data does not load into the view.
App delegate.h:
#interface dmbAppDelegate : UIResponder <UIApplicationDelegate, UITabBarControllerDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) UITabBarController *tabBarController;
#end
AppDelegate.m:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
UIViewController *viewController1 = [[[dmbFirstViewController alloc] initWithNibName:#"dmbFirstViewController" bundle:nil] autorelease];
UIViewController *viewController2 = [[[dmbSecondViewController alloc] initWithNibName:#"dmbSecondViewController" bundle:nil] autorelease];
self.tabBarController = [[[UITabBarController alloc] init] autorelease];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:viewController1, viewController2, nil];
NSLog(#"Loading first tab view from app delegate...");
self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}
FirstViewController.h:
#interface dmbFirstViewController : UIViewController {
ReservationsTable *reservationsController;
WaitlistTable *waitlistController;
IBOutlet UITableView *reserveTable;
IBOutlet UITableView *waitlistTable;
}
FirstViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(#"FirstView Controller - View Loading Started");
[reserveTable setDataSource:reservationsController];
[waitlistTable setDataSource:waitlistController];
NSLog(#"FirstView Controller - Loading Table Views..");
[reserveTable setDelegate:reservationsController];
[waitlistTable setDelegate:waitlistController];
reservationsController.view = reservationsController.tableView;
waitlistController.view = waitlistController.tableView;
NSLog(#"FirstView Controller - View Loading Finished");
}
Both the tables have a .h and .m with the standard table methods implemented. I also added 2 tables in the first view nib file and linked them to the file owner.
Update:
ReserveTable.h:
#interface WaitlistTable : UITableViewController <UITableViewDataSource, UITableViewDelegate>{
NSMutableArray *waitlistitems;
}
ReserveTable.m:
- (void)viewDidLoad
{
NSLog(#"View Did Load - Wait List Table");
waitlistitems = [[NSMutableArray arrayWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",#"6",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",nil] retain];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
NSLog(#"Inside number of section for Wait List table...");
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSLog(#"Inside numberofRows for Wait List table...");
// Return the number of rows in the section.
return [waitlistitems count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Inside cell for row at index path for Wait List table...");
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
cell.textLabel.text = [NSString stringWithFormat:#"1.%#" ,[waitlistitems objectAtIndex:indexPath.row]];
return cell;
}
Thoughts?
Change your viewDidLoad to this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
reservationsController = [[ReservationsTable alloc] init];
waitlistController = [[WaitlistTable alloc] init];
NSLog(#"FirstView Controller - View Loading Started");
[reserveTable setDataSource:reservationsController];
[waitlistTable setDataSource:waitlistController];
NSLog(#"FirstView Controller - Loading Table Views..");
[reserveTable setDelegate:reservationsController];
[waitlistTable setDelegate:waitlistController];
NSLog(#"FirstView Controller - View Loading Finished");
}
basically, you are never initializing your tableViewControllers (I guess that is the name of both of them, I would change their names to something like "WaitlistTableViewController" and "ReservationsTableViewController", but that is just me.) Also, setting the 'tableView' to the 'view' is unnecessary.
Or even better, initialize them in the init method for your dmbFirstViewController.
Or just use dmbFirstViewController like this:
dmbFirstViewController.h:
#interface dmbFirstViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
ReservationsTable *reservationsController;
WaitlistTable *waitlistController;
IBOutlet UITableView *reserveTable;
IBOutlet UITableView *waitlistTable;
NSMutableArray *waitlistitems;
NSMutableArray *reserveitems;
}
dmbFirstViewController.m:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
waitlistitems = [[NSMutableArray arrayWithObjects:#"1",#"2",#"3",#"4",#"5",#"6",#"6",#"8",#"9",#"10",#"11",#"12",#"13",#"14",#"15",#"16",#"17",nil] retain];
NSLog(#"FirstView Controller - View Loading Started");
[reserveTable setDataSource:self];
[waitlistTable setDataSource:self];
NSLog(#"FirstView Controller - Loading Table Views..");
[reserveTable setDelegate:self];
[waitlistTable setDelegate:self];
NSLog(#"FirstView Controller - View Loading Finished");
}
- (void)viewDidAppear:(BOOL)animated
{
[reserveTable reloadData];
[waitlistTable reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
NSLog(#"Inside number of section for Wait List table...");
if(tableView == waitlistTable)
{
//Return sections for waitlistTable
return 1;
}else{
//Return sections for reservedTable
return 1;
}
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(tableView==waitlistTable)
{
NSLog(#"Inside numberofRows for Wait List table...");
// Return the number of rows in waitlistTable section.
return [waitlistitems count];
}else{
// Return the number of rows in reservedTable section.
return [reserveditems count];
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(tableView == waitlistTable)
{
NSLog(#"Inside cell for row at index path for Wait List table...");
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
// Configure the cell...
cell.textLabel.text = [NSString stringWithFormat:#"1.%#" ,[waitlistitems objectAtIndex:indexPath.row]];
return cell;
} else {
//Create cell for reservedTable Cell
.....
return cell;
}
}
You'll have to finish off the part about reservedTable cells, I didn't have that code. Plus, I guessed on the items array for reservedTable and did not initialize it.
I think in your firstViewController you should push the views.
Assuming that reserveTable is your main table, push it like so
[self.view addSubView:reserveTable];
Also, did you import the ReserveTable.h to your firstViewController?
If yes, you should initialize the table there though.
But what i suggest, though, is to transform the firstViewController in a tableViewController, editing the App Delegate and the firstViewController.h and m, and from there initialize the table (even adding the other one too). So that would be easier!

Error -[NSIndexPath release]: message sent to deallocated instance 0x6a9d790

I create tableView with my custom cells.
here the code of the cell .h file
#interface ZUITableViewCell : UITableViewCell {
IBOutlet UITextField *_textFieldOutlet;
IBOutlet UILabel *_textLabelOutlet;
}
#property (retain, nonatomic) IBOutlet UITextField *_textFieldOutlet;
#property (retain, nonatomic) IBOutlet UILabel *_textLabelOutlet;
#end
And in my tableview controller file I create this
#import <UIKit/UIKit.h>
#import "ZUITableViewCell.h"
#protocol ZUITableViewControllerDelegate ///for main view controller
-(void) doneButtonPressed;
#end
#interface ZUITableViewController : UITableViewController {
id <ZUITableViewControllerDelegate> delegate;
NSMutableArray *menuItems;
BOOL isAddingFields;
}
#property (nonatomic, retain) NSArray *menuItems;
#property (nonatomic, assign) id<ZUITableViewControllerDelegate> delegate;
- (CGRect) GetCellHegiht: (ZUITableViewCell *) cell;
#end
I want to add new rows in my table view when user select the second row.
To make this I describe this method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
*/
NSLog (#"row = %d", indexPath.row);
NSLog(#"INIT %p", self);
ZUITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[cell._textFieldOutlet becomeFirstResponder];
if (indexPath.row == 1 && isAddingFields == FALSE) {
isAddingFields = TRUE;
[menuItems insertObject:#"CC" atIndex:1];
[menuItems insertObject:#"BCC" atIndex:2];
NSIndexPath *path1 = [NSIndexPath indexPathForRow:1 inSection:0];
NSIndexPath *path2 = [NSIndexPath indexPathForRow:2 inSection:0];
NSArray *indexArray = [NSArray arrayWithObjects:path1,path2,nil];
[tableView insertRowsAtIndexPaths:indexArray withRowAnimation:UITableViewRowAnimationTop];
[tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[path1 release];
[path2 release];
[cell._textFieldOutlet becomeFirstResponder];
}
}
and there is my cellForRowAtIndexPath method
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
NSLog(#"INIT %p", self);
NSLog (#"cell for row = %d", indexPath.row);
ZUITableViewCell *cell = (ZUITableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSArray *topLevelsObjects = [[NSBundle mainBundle] loadNibNamed:#"ZUITableViewCell" owner:nil options:nil];
for (id currentObject in topLevelsObjects){
if ([currentObject isKindOfClass:[UITableViewCell class]]){
cell = (ZUITableViewCell *) currentObject;
break;
}
}
}
if (indexPath.row == 3 && isAddingFields == FALSE){
// [cell setBounds:[self GetCellHegiht:cell]];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell._textLabelOutlet.text = [menuItems objectAtIndex:indexPath.row];
cell._textFieldOutlet.tag = indexPath.row;
[cell._textFieldOutlet setEnabled:YES];
if(indexPath.row == 0){
[cell._textFieldOutlet becomeFirstResponder];
}
return cell;
}
In debug mode as I can see after clicking to the row with indexPath.row == 1 this controller call method cellForRowAtIndexPath two times with indexPath.row = 1 and 2. But After It controller call method
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//#warning Incomplete method implementation.
return [menuItems count];
}
again and method cellForRowAtIndexPath calling too, After program take me this error (I enable zombie) Error -[NSIndexPath release]: message sent to deallocated instance 0x6a9d790
The method [NSIndexPath indexPathForRow:inSection:] (used in path1 and path2) returns an autoreleased object, no need to call release after.