Segue Headaches in Xcode - iphone

I have a tableview and I created a segue to push it to another view controller. Every now an then this segue breaks without me touching it I can guarantee. I didn't even edit the file I put it in.
My Code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *acell = [tableView cellForRowAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"cellWasSelected" sender:acell];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"cellWasSelected"])
{
if ([sender isKindOfClass:[UITableViewCell class]])
{
UITableViewCell *selectedCell = sender;
ViewController *myDetViewCont = segue.destinationViewController;
myDetViewCont.navigationItem.title = selectedCell.textLabel.text;
}
}
}
First after clicking a cell xcode directs me to the file this segue pushes to.
It redirects me here: action:#selector(handleSingleTap:)];
I use this for my images to trigger this: [self.navigationController popToRootViewControllerAnimated:YES]
If I choose in Thread 1 my main view I see the problem is:
[self performSegueWithIdentifier:#"cellWasSelected" sender:acell];
But whats the problem I used this athousand times and it starts crashing without me changing it.

Your code don't have any bugs, the problem is somewhere else. May be you have not give the segue an identifier in interface builder. Try this code. It is working on my side, if your files don't have any problem it should run on your side too.
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSString* identifier = [segue identifier];
if ([identifier isEqualToString:#"cellWasSelected"]) {
NSLog(#"Performing Seque");
if ([sender isKindOfClass:[UITableViewCell class]]) {
NSLog(#"correct");
UITableViewCell *selectedCell = sender;
UIViewController* myDetViewCont = segue.destinationViewController;
myDetViewCont.navigationItem.title = selectedCell.textLabel.text;
}
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"%#",#"Cell Selected");
UITableViewCell* cell = [tableView cellForRowAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"cellWasSelected" sender:cell];
}
P.S. If you are still having problems you, then may be you are new to storyboard and you need enough knowledge to work on them.
http://www.raywenderlich.com/5138/beginning-storyboards-in-ios-5-part-1

Related

UITableView prepareForSegue assigning indexPath to sender?

In the code below I am just trying out setting up a segue directly from the UIViewController to a DetailViewController. I usually do the segue from the UITableViewCell (which I think is clearer) but this time I just wanted to try going directly from the ViewController using -tableView:didSelectRowAtIndexPath:
My question is: I got to a situation where I needed to access the UITableViewCell indexPath from within -prepareForSegue:sender: my solution was to send the indexPath via sender. Usually I would set sender to self here, I have looked around and I can't find any reference to this, I am just curious if using sender to pass an indexPath is acceptable? I am pretty sure it is, and I am sure I have read that you can somewhere before, but I can't find any reference to it now.
// ------------------------------------------------------------------- **
// DELEGATE: UITableView
// ------------------------------------------------------------------- **
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"%s", __PRETTY_FUNCTION__);
[self performSegueWithIdentifier:#"SEGUE_TWO" sender:indexPath];
}
// ------------------------------------------------------------------- **
// SEGUE: ViewController_ONE > DetailViewController
// ------------------------------------------------------------------- **
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSLog(#"%s", __PRETTY_FUNCTION__);
if([[segue identifier] isEqualToString:#"SEGUE_TWO"]) {
NSUInteger cellRow = [(NSIndexPath *)sender row];
DetailViewController *destinationController = [segue destinationViewController];
[destinationController setDataString:[[self dataList] objectAtIndex:cellRow]];
}
}
EDIT: What I was looking for in this case was
NSIndexPath *indexPath = [[self tableView] indexPathForSelectedRow];
essentially a way to get indexPath into -performForSegue:sender: without having to pass it via the sender parameter (in -performSegueWithIdentifier:sender:)
I typically avoid using the sender in prepareForSegue: because it could be anything. This makes asserting that your sender is what you are expecting more cumbersome and fragile. Also, I prefer to check for the class of the destinationViewController object rather than the segue name. Your functionality is tied to your object (or more specficially its interface), not the title of your segue, so checking your class makes it clear what you are trying to accomplish.
Below is the usual template I use in production when segueing from a UITableView. Note that you should always check if the destinationViewController is embedded in a nav controller and then check the class of the object as well to make sure you are handling it correctly. Finally, use an assert to ensure that somewhere down the line you haven't added some new segue that you forgot to handle.
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
// unwrap the controller if it's embedded in the nav controller.
UIViewController *controller;
UIViewController *destVC = segue.destinationViewController;
if ([destVC isKindOfClass:[UINavigationController class]])
{
UINavigationController *navController = (UINavigationController *)destVC;
controller = [navController.viewControllers firstObject];
}
else
{
controller = destVC;
}
if ([controller isKindOfClass:[DetailViewController class]])
{
DetailViewController *vc = (DetailViewController *)controller;
NSIndexPath *ip = [self.tableView indexPathForSelectedRow];
[vc setDataString:[[self dataList] objectAtIndex:ip.row]];
}
else
{
NSAssert(NO, #"Unknown segue. All segues must be handled.");
}
}
You could use UITableView's method indexPathForCell. I hope you have a tableView instance variable.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if([sender isKindOfClass:[UITableViewCell class]]) {
NSIndexPath * indexPath = [self.tableView indexPathForCell:sender];
//Your code here
}
}

How to pass the properties of data to anotherViewController?(StoryBoard)

i want to pass the properties of data after select a tableviewcell.Please guide me.i already setup the detailviewcontroller.DataArray is my mutablearray in detailviewcontroller.peripheralManager is my NSbject.Peripheral is my device.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
peripheralManager=[[PeripheralManager alloc]init];
self.MeBle.enabled=NO;
device=[[NSMutableArray alloc]init];
}
- (void)peripheralManagerDidConnectPeripheral:(PeripheralManager *)peripheral
{
NSLog(#"%#",peripheral.deviceName);
[device addObject:[NSString stringWithFormat:#" %# ",peripheral.deviceName]];
// [device addObject:peripheral];
[self.deviceTable reloadData];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier=#"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if(cell==nil)
{
cell =[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.textLabel.text=[device objectAtIndex:indexPath.row];
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//DetailViewController *detail=[self.storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];
// [self.navigationController pushViewController:detail animated:YES];
//[self performSegueWithIdentifier:#"TableDetails" sender:[device objectAtIndex:indexPath.row]];
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"TableDetails"]) {
DetailViewController *detail=segue.destinationViewController;
detail.dataArray=device;
}
}
in the prepareForSegue you should get the current selection index:
NSIndexPath *indexPath = [self.deviceTable indexPathForCell:sender];
now, you may either pass just this index to the destination controller OR specific object from you current array, its up to you.
detail.idx = [indexPath row];
Edit: there you don't need to implement
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
just in
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
change to this:
detailViewObject.objCurrentDevice=[device objectAtIndex:[[self.deviceTable indexPathForCell:sender] row]];
In interface builder, check out the seague runtime attributes section, u can create multiple cell same style but different identifier then cellForRowAtIndexPath return the cell by its row

How do I change a UILabel's text on a pushed (with segue) view, relative to the senders selected accessory?

I have a UITableView that pushes, via a storyboard segue, a view, which displays a UILabel that I wish to change the text on relative to the indexPath.row of the selected accessory on the UITableView.
I know it's probably wildly wrong, but this was my attempt. I feel like I'm going about it very wrong:
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"ArticlePreviewSegue" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = [sender indexPathForSelectedRow];
ArticlePreviewViewController *apvc = [segue destinationViewController];
NSDictionary *article = [_newsFetcher.articles objectAtIndex:indexPath.row];
apvc.titleLabel.text = [article objectForKey:#"title"];
apvc.bodyLabel.text = [article objectForKey:#"body"];
}
Thanks you!
One problem may be that tapping the accessory doesn't select the row. You can handle that by passing the index path as the sender of the segue:
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"ArticlePreviewSegue" sender:indexPath];
}
Now you can access the index path in prepareForSegue:sender: without relying on the row being selected.
Another problem is that in prepareForSegue:sender:, apvc hasn't loaded its view yet. So apvc.titleLabel and apvc.bodyLabel are both nil.
The proper way to handle this is to give ArticlePreviewViewController an article property and set that property in prepareForSegue:sender:, like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *indexPath = (NSIndexPath *)sender;
ArticlePreviewViewController *apvc = [segue destinationViewController];
apvc.article = [_newsFetcher.articles objectAtIndex:indexPath.row];
}
Then, in -[ArticlePreviewViewController viewDidLoad], you can set the labels based on the article:
- (void)viewDidLoad {
[super viewDidLoad];
self.titleLabel.text = self.article[#"title"];
self.bodyLabel.text = self.article[#"body"];
}

Passing Segue Gives Error

Hi I have currently made a push on select to get my table view to push to a detail view. I also have this code to send the name of the table cell that was selected:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *acell = [tableView cellForRowAtIndexPath:indexPath];
selectedCell = acell.textLabel.text;
DetailViewController *myDetViewCont = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:[NSBundle mainBundle]];
myDetViewCont.navigationItem.title = selectedCell;
[self.navigationController pushViewController:myDetViewCont animated:YES];
}
It builds successfully but in the simulator It throws an error:
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
Thread: 1 signal: SIGABRT
I've looked up similar problems but havent found a solution.
Any Help?
Ok additional info on the error:
[self.navigationController pushViewController:myDetViewCont animated:YES];
If you're using storyboards, then the following line doesn't make sense:
DetailViewController *myDetViewCont = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:[NSBundle mainBundle]];
Do you have a NIB file called DetailViewController.xib in your project? Presumably not. Thus myDetViewCont will be nil and you'll get an exception. If you do have that NIB, then what is the push segue going between (because you can't segue from a storyboard to a NIB)?
Assuming that you really want to use storyboards, and not NIBs, if you already have a push segue between the controllers, give that segue an identifier (you do this in Interface Builder; in my code below I'll just use youridentifier as a placeholder for whatever you specify, which you'll replace with the correct identifier) and then you should transition using the segue:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *acell = [tableView cellForRowAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"youridentifier" sender:acell];
}
If you want to send data to the new controller, you'd use prepareForSegue to do that:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"youridentifier"])
{
if ([sender isKindOfClass:[UITableViewCell class]])
{
UITableViewCell *selectedCell = sender;
UIViewController *myDetViewCont = segue.destinationViewController;
myDetViewCont.navigationItem.title = selectedCell.textLabel.text;
}
else
{
NSLog("%s Was expecting sender to be a tableviewcell", __FUNCTION__);
}
}
}
Found the solution to this. You need to have a UINavigationController in your story board. To do this take your first scene, click on it and go to tabs at top of xcode and do Editor>Embed in>Navigation controller, check the button to set the navigation controller as inital scene. Add the following code to hide the navbar at top.
self.navigationController.navigationBar.hidden = YES;
self.navigationController.navigationBar.translucent = NO;

update UILabel with the content of UICell from a different view

I have an NSArray tablePeople which makes up my UITableView on my 1st View Controller PeopleController. I have a UILabel personsName on my second View Controller PeopleDetailsController which I want to update with the contents of cell.textLabel.text of each row in my TableView. I have this method but it's not working:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
((PeopleController *)segue.destinationViewController).delegate=self;
if ([[segue identifier] isEqualToString:#"toPeopleArticle"]) {
NSIndexPath *indexPath = (NSIndexPath *)sender;
PeopleDetailsController *mdvc = segue.destinationViewController;
mdvc.personsName.text = [self.tablePeople objectAtIndex: indexPath.row];
}
}
I also have this code when the cell is selected:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"toPeopleArticle" sender:indexPath];
}
I have to note that PeopleDetailsController is a modal view and PeopleController is a navigation view controller.
EDIT:
The text on the UILabel on the 2nd VC is just not being updated, it stays the same, that's the whole problem.
Change the following
PeopleDetailsController *mdvc = segue.destinationViewController;
mdvc.personsName.text = [self.tablePeople objectAtIndex: indexPath.row];
to
PeopleDetailsController *mdvc = segue.destinationViewController;
mdvc.personsNameString = [self.tablePeople objectAtIndex: indexPath.row];
where personsNameString is a property of type NSString in the PeopleDetailsController
Now in PeopleDetailsController viewDidLoad or viewWillAppear Function set the value of the label to the value of the property
mdvc.personsName.text = personsNameString;