UINavigationController not pushing - objective-c

I have two UITableViews embedded in a UINavigationController. The selection of a cell in the first table viewcontroller should trigger a push segue to the second. This segue is not being performed.
FirstTableVC
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// getting called as planned
if (indexPath.row == 0 && reload == YES) {
// working
[self pushedRefresh:self];
reload = NO;
return;
}
NSLog(#"past return"); // logged as planned
// NOT performing!
[self performSegueWithIdentifier:#"more" sender:[self.tableView cellForRowAtIndexPath:indexPath]];
}
What I've done/noticed
SecondTblVC's viewDidLoad: method is not being called. However, if I use non-push segues, the VC is presented correctly.
I've tried using nil and self in the sender parameter for performSegueWithIdentifier: with the same results.
I've converted the second view controller into a vanilla UIViewController with the same results.
The "more" segue is correctly labeled and connected as push
There's no crash, just lack of segue.
I've deleted the entire UINavigationController setup in storyboard and replaced it with the same results.
I do not have a separate .h/.m for the UINavigationController
Table view delegates/data source links are working as planned.
There is a destinationViewController (learned from logging in performSegue method).
Please let me know if more code and screenshots would help.
Thank you.
Edit
Here's my cellForRowAtIndexPath implementation:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"ReuseCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
cell.textLabel.text = [clubNames objectAtIndex:indexPath.row];
cell.detailTextLabel.text = [times objectAtIndex:indexPath.row];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:simpleTableIdentifier];
}
[cell setAccessoryType:UITableViewCellAccessoryDisclosureIndicator];
return cell;
}

Try using table view controller instead of table view
In your storyboard, create a storyboard ID for each of the UITableViewControllers, i.e.: vc1 and vc2
In your vc1 function, lookup the storyboard ID for the second table view controller,
which is :
UITableViewController *vc2 = [self.storyboard
instantiateViewControllerWithIdentifier:#"vc2"];
[self.navigationController pushViewController:vc2 animated:YES];

Related

How to call a method in the parent view controller from a cell of a cell?

The structure of my app currently looks like this:
Collection View Controller -> Generic Cell with table view inside of it -> individual cells.
I would like to call a method in the collection view controller from one of the individual cells. So far I have implemented a delegate in the individual cell but if I can't seem to set my delegate in the collection view controller because I don't have an instance of it.
Furthermore, I have several cells inside the table view that are required to access the methods in the collection view controller.
The responder chain can help.
The view can query the responder chain for the first target that can accept a message. Suppose the message is -fooBar, then the view can query the target using the method -[UIResponder targetForAction:sender:]
// find the first responder in the hierarchy that will respond to -fooBar
id target = [self targetForAction:#selector(fooBar) sender:self];
// message that target
[target fooBar];
Note that this communication is controlled by this method:
(BOOL)canPerformAction:(SEL)action
withSender:(id)sender;
This default implementation of this method returns YES if the responder class implements the requested action and calls the next responder if it does not.
By default, the first object that responds to that message will become the target so you may want to override the canPerformAction:withSender: if needed for some views or view controllers.
For that you can do like that :
In Collection View Controller -> .h file
#interface CollectionViewController : UICollectionViewController<ColectionCellDelegate>
#end
In Collection View Controller -> .m file
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
return [self.collectionData count];
}
// The cell that is returned must be retrieved from a call to -dequeueReusableCellWithReuseIdentifier:forIndexPath:
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
CollectionCell *cell = (CollectionCell *)[collectionView dequeueReusableCellWithReuseIdentifier:#"CollectionCell" forIndexPath:indexPath];
cell.cellData = [self.collectionData objectAtIndex:indexPath.row];
cell.delegate = self;
return cell;
}
-(void)tableCellDidSelect:(UITableViewCell *)cell{
NSLog(#"Tap %#",cell.textLabel.text);
DetailViewController *detailVC = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
detailVC.label.text = cell.textLabel.text;
[self.navigationController pushViewController:detailVC animated:YES];
}
In CollectionCell.h
#class CollectionCell;
#protocol ColectionCellDelegate
-(void)tableCellDidSelect:(UITableViewCell *)cell;
#end
#interface CollectionCell : UICollectionViewCell<UITableViewDataSource,UITableViewDelegate>
#property(strong,nonatomic) NSMutableArray *cellData;
#property(weak,nonatomic) id<ColectionCellDelegate> delegate;
#end
In CollectionCell.m
#implementation CollectionCell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.cellData = [[NSMutableArray alloc] init];
}
return self;
}
-(void) awakeFromNib{
[super awakeFromNib];
self.cellData = [[NSMutableArray alloc] init];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [self.cellData count];
}
// Row display. Implementers should *always* try to reuse cells by setting each cell's reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
// Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"TableCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
cell.textLabel.text = [self.cellData objectAtIndex:indexPath.row];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[[self delegate] tableCellDidSelect:cell];
}

Pushing to a Detail View from a Table View Cell using Xcode Storyboard

I have a table view inside a View Controller. I can populate all my information inside the table view. However I am a bit lost for setting up the detail views. I believe each table cell needs a segue to a each detail view but not completely sure.
Here is my code. What am I missing to accomplish the segue from the table view to the detail views?
Code:
.h
#interface DetailViewController : UIViewController <UITableViewDelegate,UITableViewDataSource>
{
IBOutlet UITableView *myTable;
NSMutableArray *contentArray;
}
#property (strong, nonatomic) IBOutlet UITableView *myTable;
.m
- (void)viewDidLoad
{
contentArray = [[NSMutableArray alloc]init];
[contentArray addObject:#"Espresso"];
[contentArray addObject:#"Latte"];
[contentArray addObject:#"Capicino"];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
//Table Information
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [contentArray count];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if([[contentArray objectAtIndex:indexPath.row]isEqualToString:#"EspressoViewController"])
{
EspressoViewController *espresso = [[EspressoViewController alloc]initWithNibName:#"EspressoViewController" bundle:nil];
[self.navigationController pushViewController:espresso animated:YES];
}
else if ([[contentArray objectAtIndex:indexPath.row] isEqualToString:#"Latte"])
{
LatteViewController *latte = [[LatteViewController alloc] initWithNibName:#"Latte" bundle:nil];
[self.navigationController pushViewController:latte animated:YES];
}
}
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
[self tableView:tableView didSelectRowAtIndexPath:indexPath];
}
- (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"];
}
NSString *cellValue = [contentArray objectAtIndex:indexPath.row];
cell.textLabel.text = cellValue;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.font = [UIFont systemFontOfSize:16];
cell.detailTextLabel.text = #"Hot and ready";
UIImage *image = [UIImage imageNamed:#"coffeeButton.png"];
cell.imageView.image = image;
cell.textLabel.text = [contentArray objectAtIndex:indexPath.row];
return cell;
}
I think you made this a little too complicated. Don't worry, I do the same thing often.
First, by sending tableView:didSelectRowAtIndexPath: from within tableView:accessoryButtonTappedForRowAtIndexPath: there is no difference between the two methods. Tapping the cell, or it's accessory button performs the same action. If you don't need the accessory button to perform a different action than tapping the cell itself, remove it.
Second, if you're using a storyboard, you do not need to alloc/initWithNib for your view controllers. Instead, use a segue. If you do this through the storyboard, you also don't need to programmatically push viewControllers onto your navigationController
Build your storyboard first:
Drag out a UITableViewController. Make sure you set the class of the UITableViewController you dragged out to your own "DetailViewController" using the inspector pane on the right side.
Then select this controller and using the menus choose "Editor->Embed In->Navigation Controller".
Next, drag out three generic UIViewControllers. Set the class of one to "LatteViewController", another to "EspressoViewController", and a third to "CapicinoViewController" (using the inspector again).
Control+drag from the UITableViewController over to each of these viewControllers and choose PUSH.
Click on the little circle that's on the arrow between your UITableViewController and each of these viewControllers. In the inspector (on the right side), give each segue a unique name in the Identifier field. You will need to remember this name for your code. I would name them "EspressoSegue", "LatteSegue", and "CapicinoSegue". You'll see why in the code below.
Then put the following code in your UITableViewController:
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
//Build a segue string based on the selected cell
NSString *segueString = [NSString stringWithFormat:#"%#Segue",
[contentArray objectAtIndex:indexPath.row]];
//Since contentArray is an array of strings, we can use it to build a unique
//identifier for each segue.
//Perform a segue.
[self performSegueWithIdentifier:segueString
sender:[contentArray objectAtIndex:indexPath.row]];
}
How you implement the rest is up to you. You may want to implement prepareForSegue:sender: in your UITableViewController and then use that method send information over to segue.destinationViewController.
Note that I passed the string from your contentArray as the sender for the segue. You can pass whatever you like. The string that identifies the cell seems like the most logical information to pass, but the choice is up to you.
The code posted above should perform the navigation for you.

Detailtext is not showing in case?

I have a case but the detailtext is not showing? Does anyone know what te problem is?
i used: UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UIViewController *controller;
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
switch(indexPath.row) {
case 0:
{
NSLog(#"Case 0");
controller = [[wed1 alloc] init]; //WithStyle:UITableViewStylePlain];
[self.navigationController pushViewController:controller animated:YES];
cell.detailTextLabel.text = #"Wedstrijden!";
}
break;
case 0:
{
NSLog(#"Case 0");
controller = [[wed1 alloc] init]; //WithStyle:UITableViewStylePlain];
//created a controller
[self.navigationController pushViewController:controller animated:YES];
//at his point, you are showing next ViewController's view
cell.detailTextLabel.text = #"Wedstrijden!";
//here you are changing the text of the text for current tableView's cell (which is actually going to be off the screen as new controller's view will be shown after previous statement execution)
}
with the piece of code you shared - I don't find anything wrong here, the detail text should not be visible as this view will be gone.
I don't fully understand why you'd want to set the detail text on a cell after pushing a new view controller. As samfisher said, when the user presses the first cell, in case 0, the new view controller will be alloc'd, init'd and then pushed, and after that you set the detail text to "Wedstrijden".
I'm assuming you're probably just confused between which property to use. My question to you is: when and where do you want this text to appear?
If the cell was created with the style UITableViewCellStyleDefault, the detail text label isn't displayed. In tableView:cellForRowAtIndexPath: implementation, you may create the cell with style UITableViewCellStyleSubtitle or UITableViewCellStyleValue1.
Use UIListContentConfiguration instead, or subtitle won't show.
#import <UIKit/UIListContentConfiguration.h>
And in the cellForRowAtIndexPath method:
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"AddressCell" forIndexPath:indexPath];
if(cell==nil){
cell=[[UITableViewCell alloc]initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:#"AddressCell"];
}
NSRange range = [self.dataArray[indexPath.row] rangeOfString:#","];
NSString *street;
NSString *restAddress;
if (range.location != NSNotFound) {
street = [self.dataArray[indexPath.row] substringToIndex:range.location];
restAddress= [self.dataArray[indexPath.row] substringFromIndex:range.location + 1];
}
UIListContentConfiguration *configuration = cell.defaultContentConfiguration;
configuration.text=street;
configuration.image=kImage(#"Location");
configuration.secondaryText=restAddress;
cell.contentConfiguration = configuration;
return cell;
}
Will look like this:

Multi-View TableView with RootController

I am trying to create a multi-view app with navigation template. I want table on the bottom part of initial view, with other views (image view, labels, etc) on the top.
Originally I modified RootViewController.xib to resize tableview, and added image view, but nothing displayed except full-size table.
So I modified RootViewController.xib to add UIView, then added a UITableView and UIImageView to that view. I added IBOutlet for the tableView. In my RootViewController.xib, File'S Owner (RootViewController) has outlet connection to the view and the table view. When I right-click the UITableView, I see the referencing outlet to File's Owner. When I right-click the UIView, I see the referencing outlet to File's Owner. (I don't have an outlet to UIImageView, since I don't modify in code; do I need one?).
However, when I launch the app, it crashes with message:
'NSInternalInconsistencyException',
reason: '-[UITableViewController loadView] loaded the "RootViewController" nib but didn't get a UITableView.'
Here is the cellForRowAtIndexPath method:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *QuizIdentifier = #"QuizIdentifier";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:QuizIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:QuizIdentifier]
autorelease];
}
// Configure the cell.
UIImage *_image = [UIImage imageNamed:#"icon"];
cell.imageView.image = _image;
NSInteger row = [indexPath row];
cell.textLabel.text = [_categories objectAtIndex:row];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
You're getting that error because when you create a navigation-based iOS app, Xcode makes the RootViewController a subclass of UITableViewController. Once you combine that UITableView with a normal UIView, your subclass no longer complies with the UITableViewController class. So, in your RootViewController.m and .h files change UITableViewController to UIViewController. You may also need to change a few other things in your init method, but let's just start with this.
Try this:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"\n\nI'm in Root / cellForRowAtIndexPath");
static NSString *QuizIdentifier = #"QuizIdentifier";
UITableViewCell *cell =
[tableView dequeueReusableCellWithIdentifier:QuizIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:QuizIdentifier]
autorelease];
UIImage *_image = [UIImage imageNamed:#"icon"];
cell.imageView.image = _image;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
}
// Configure the cell.
NSInteger row = [indexPath row];
cell.textLabel.text = [_categories objectAtIndex:row];
NSLog(#" We are at row %i with label %# ", row, cell.textLabel.text);
return cell;
}

2 tableview on a single view

I need an example or explanations of how to populate 2 table views which are on the same view. I need to understand the "cellForRowAtIndexPath" method, could someone provide me an example on how should the code be?
I mean how to identify which goes which table view?
Thanks
Below is my cellForRowAtIndexPath method:
// 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];
}
// Configure the cell...
// Set up the cell
MyAppAppDelegate *appDelegate = (MyAppAppDelegate *)[[UIApplication sharedApplication] delegate];
if (tableView == radios_tv) { //radio_tv is an IBOutleet UITableView
sqlClass *aRadio = (sqlClass *)[appDelegate.array_radios objectAtIndex:indexPath.row];
[cell setText:aRadio.r_name];
return cell;
}
if (tableView == presets_tv) { //preset_tv is an IBOutlet UITableView
}
}
and hey vikingsegundo, now I need to delete a cell which is on my TableViewController class, how do I do this? I explain, here is my code:
- (void)tableView:(UITableView *)tv commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath {
if(editingStyle == UITableViewCellEditingStyleDelete) {
//Get the object to delete from the array.
Coffee *coffeeObj = [appDelegate.coffeeArray objectAtIndex:indexPath.row];
[appDelegate removeCoffee:coffeeObj];
//Delete the object from the table.
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
}
}
Since we put different controllers, how should we proceed for this line? Should I put the tableViewController instead of the "self"?
//Delete the object from the table.
[self.tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
IMO the cleanest solution would be to have one controller for each tableview.
radios_tv would call it own delegate's method, while presets_tv calls it own.
edit
if you use one controller for n tableview, you will have to use if-statemenst in many places,
in
– numberOfSectionsInTableView:
– tableView:numberOfRowsInSection:
– tableView:titleForHeaderInSection:
…
basically in all UITableViewDatasource-Protocol methods that you will need to implement.
So if you need to change something, you have to change it in many places.
If you use one controller class for one tableview, you won't have to check at all.
write a controller class for every tableview, make it conforming to the UITableViewDatasource protocol
implement the protocol methods you will need. at least
– numberOfSectionsInTableView:,
– tableView:numberOfRowsInSection:,
– tableView:cellForRowAtIndexPath:
call -setDataSource:for every tableview to an object of the right controller class
I think, it was shown in one of the WWDC 2010 videos. I am not sure, but I guess it was Session 116 - Model-View-Controller for iPhone OS.
edit
I wrote an example code: http://github.com/vikingosegundo/my-programming-examples
On one view controller if you have to use two tables then you can set IBOutlet to both tables or assigns different tag to them so when you use following cellForRowAtIndexPath you can differentiate in both tables as below
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCellStyle style =UITableViewCellStyleSubtitle;
static NSString *MyIdentifier = #"MyIdentifier";
DataListCell *cell = (DataListCell*)[tableView dequeueReusableCellWithIdentifier:MyIdentifier];
cell = [[DataListCell alloc] initWithStyle:style reuseIdentifier:MyIdentifier];
cell.selectionStyle=UITableViewCellSelectionStyleNone;
if(tableView==tblLanguage)//tblLanguage IBOutlet for first table
{
if ((selectedIndexPath != nil) && (selectedIndexPath.row == indexPath.row))
{
UIImageView *imgView=[[UIImageView alloc]initWithFrame:CGRectMake(320-30, 9, 22, 15)];
imgView.image=[UIImage imageNamed:#"btn_Expand.png"];
[cell addSubview:imgView];
tblSongs.hidden=NO;
tblSongs.frame=CGRectMake(0,42, 320, ([arrSongListForSpecificLanguage count]*40));
[cell addSubview:tblSongs];
}
else
{
UIImageView *imgView=[[UIImageView alloc]initWithFrame:CGRectMake(320-30, 9, 16, 22)];
imgView.image=[UIImage imageNamed:#"btn_Collaps.png"];
[cell addSubview:imgView];
}
cell.lblCustomerName.textColor=[UIColor blackColor];
cell.lblCustomerName.font=[UIFont boldSystemFontOfSize:16];
//set the first label which is always a NamesArray object
[cell setcustomerName:[objAppDelegate.viewController.arrLanguage objectAtIndex:indexPath.row]];
}
else //for other table
{
ParseData *objParse;
objParse=[arrSongListForSpecificLanguage objectAtIndex:indexPath.row];
cell.lblCustomerName.textColor=[UIColor blackColor];
cell.lblCustomerName.frame=CGRectMake(cell.lblCustomerName.frame.origin.x, cell.lblCustomerName.frame.origin.y, 310, cell.lblCustomerName.frame.size.height);
//set the first label which is always a NamesArray object
[cell setcustomerName:objParse.track];
}
return cell;
}
}
You can also use tag for the same in which your if statement as below
if(tableView.tag==1)//tblLanguage tag=1
Similar if statement use for other delegate & datasource methods of table