I have a problem where same view is pushed many times on selection of a row and if user keeps on clicking on the same row it crashes.
MasterDetailVC.m
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
LAClaimReport *record = [_claimReports objectAtIndex:indexPath.row];
if ([record.submitted isEqualToNumber:#1])
{ // if i click on clicking here VC is getting pushed and pushed.
[self.detailViewController showReadonlyViewForClaimReport:record];
}
else
{
[self.detailViewController showEditViewForClaimReport:record];
}
}
LADetailViewController.m
#interface LADetailViewController()
{
LAClaimReport *_claimRecord;
}
-(void) showEditViewForClaimReport:(LAClaimReport *) claimReport
{
_claimRecord = claimReport;
[self performSegueWithIdentifier:#"toEditView" sender:self];
// calls LAClaimReportViewController
}
-(void) showReadonlyViewForClaimReport:(LAClaimReport *) claimReport
{
_claimRecord = claimReport;
[self performSegueWithIdentifier:#"toReadonlyView" sender:self];
// calls LACreateReportViewController.
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"toEditView"])
{
LAClaimReportViewController *viewController = segue.destinationViewController;
viewController.claimReport = _claimRecord;
}
else if ([[segue identifier] isEqualToString:#"toReadonlyView"])
{
LACreateReportViewController *viewController = segue.destinationViewController;
viewController.claimReport = _claimRecord;
}
}
LAClaimReportViewController & LACreateReportViewController are two diff. VC having segues from LADetailViewController. I want that the VC (LACreateReportViewController or LAClaimReportViewController) should not be pushed many times on multiple click of the same row of the masterDetailVC. pls help.
You can just check if the View is already pushed. If it is then don't push it again. If its not pushed then push it.
For this you can make a instance variable may be a bool. Lets call it isPushed. Make isPushed = true; when you push the view. When you pop it then make isPushed = false;.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
LAClaimReport *record = [_claimReports objectAtIndex:indexPath.row];
if ([record.submitted isEqualToNumber:#1] && !isPushed)
{ // if i click on clicking here VC is getting pushed and pushed.
isPushed = true;
[self.detailViewController showReadonlyViewForClaimReport:record];
}
else
{
[self.detailViewController showEditViewForClaimReport:record];
}
}
The above is just an way. One thing is this: [record.submitted isEqualToNumber:#1] always false? If it is then you would like to check that I think.
Hope this will Help.
Related
I use button in ViewController to go TableViewController. In TableViewController I create int NInt1. But when I go to TableViewController condition doesn't work.
ViewController.m
- (IBAction)Button:(id)sender {
MasterViewController *trns =[[MasterViewController alloc]init];
[_Button.titleLabel.text isEqualToString:#"1"];
trns.NInt1=1;
}
TableViewController.m
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(_NInt1 == 1){
return 5;
}else{
return 20;
}
}
If you are using the story pass the value like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
RecipeDetailViewController *destViewController = segue.destinationViewController;
destViewController.NInt1 = 1;
}
Now see the value of NInt in viewDidLoad of your destination view controller.
I think you need to modify like
- (IBAction)Button:(UIButton *)sender {
MasterViewController *trns =[[MasterViewController alloc]init];
if [sender.titleLabel.text isEqualToString:#"1"]{
trns.NInt1=1;
}
}
on that tableviewController viewDidload check like use NSLog for the object is 1 or not
If you use storyboard, then the destination view controller is not created from your IBAction. It can be verified easily : you never push or present the newly created view controller, which is local in your method.
Instead use prepareForSeguemethod as described in Jaimish answer.
I tried the solution.Finally I got.Thank you.The condition has worked now.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"goTableViewController"])
{
TableViewController *tableVC = (TableViewController *) segue.destinationViewController;
tableVC.NInt1 = 5;
}
}
I am trying to set the property of a View Controller I'm segueing to to a custom object (Restroom), but it's coming out as a UITableViewCell.
Here is code from the origin View Controller that is triggering the segue. The userDidSelectRestroomNotification method is called when a user selects a cell in a table view that lists restrooms - the application is then supposed to segue to a View Controller that lists the details of that restroom:
- (void)userDidSelectRestroomNotification:(NSNotification *)notification
{
Restroom *selectedRestroom = (Restroom *)[notification object];
[self performSegueWithIdentifier:#"ShowRestroomDetails" sender:selectedRestroom];
};
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowRestroomDetails"])
{
RestroomDetailsViewController *restroomDetailsViewController =
(RestroomDetailsViewController*)segue.destinationViewController;
restroomDetailsViewController.restroom = sender;
}
}
Here is the code in my data source where the notification is posed:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// post a notification when a selection is made
NSNotification *notification = [NSNotification
notificationWithName:RRTableViewDidSelectRestroomNotification
object:[self restroomForIndexPath:indexPath]];
[[NSNotificationCenter defaultCenter] postNotification:notification];
}
- (Restroom *)restroomForIndexPath:(NSIndexPath *)indexPath
{
return [self.restroomsList objectAtIndex:[indexPath row]];
}
Point being, I am sending along a Restroom object in my notification.
The issues comes when in the RestroomDetailsViewController, I am trying to set the text of a label to the name of the Restroom object I'm passing along:
#interface RestroomDetailsViewController ()
#property (weak, nonatomic) IBOutlet UILabel *nameLabel;
#end
#implementation RestroomDetailsViewController
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.nameLabel.text = self.restroom.name;
}
#end
It crashes at self.nameLabel.text = self.restroom.name. When I inspect the objects, I see that my self.restroom object is actually a UITableViewCell. What confuses me further is that that UITableViewCell appears to be the cell that was selected by the user to trigger the segue -- i.e. the one set up in the data source:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSParameterAssert([indexPath section] == 0);
NSParameterAssert([indexPath row] < [_restroomsList count]);
UITableViewCell *restroomCell = [tableView
dequeueReusableCellWithIdentifier:restroomCellReuseIdentifier];
if(!restroomCell)
{
restroomCell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:restroomCellReuseIdentifier];
}
restroomCell.textLabel.text = [[_restroomsList objectAtIndex:[indexPath row]] name];
return restroomCell;
}
I'm not understanding why this happens and how to get that restroom property properly assigned to the Restroom object.
Why are you using postnotification ?
I don't see any requirement here to use notification. A simple function with parameter will do the work.
Try Following way to call segue
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"ShowRestroomDetails" sender:[self restroomForIndexPath:indexPath]];
}
- (Restroom *)restroomForIndexPath:(NSIndexPath *)indexPath
{
return [self.restroomsList objectAtIndex:[indexPath row]];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
Restroom *selectedRestroom = (Restroom *)sender;
if ([segue.identifier isEqualToString:#"ShowRestroomDetails"])
{
RestroomDetailsViewController *restroomDetailsViewController =
(RestroomDetailsViewController*)segue.destinationViewController;
restroomDetailsViewController.restroom = selectedRestroom ;
}
}
I understand it's not typical to have notifications being posted when cells are pressed - but that's how I'm choosing to implement this application currently.
That being said, here's how I ended up getting this to work (thanks to a comment by #rdelmar saying to check what order the methods are being called in):
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ShowRestroomDetails"])
{
if([sender isKindOfClass:[Restroom class]])
{
RestroomDetailsViewController *restroomDetailsViewController = (RestroomDetailsViewController*)segue.destinationViewController;
restroomDetailsViewController.restroom = sender;
}
}
}
I found that the prepareForSegue method was being triggered when a cell was pressed before my userDidSelectRestroomNotification method. However, I want userDidSelectRestroomNotification to actually be the method that triggers prepareForSegue because it creates the needed Restroom object. Thus I put a check in my prepareForSegue that makes sure the incoming object is a Restroom instance.
All that being said - I don't know if this is more of a hack than a solution.
I'm having an issue with my performSegueWithIdentifier, because he fires and when I get a breakpoint on the viewDidLoad of the NextViewController he enters and executes fine. Problem is there is no output and the screen continues to show the CurrentViewController. What can be the error ?
(CurrentViewController is a TableViewController)
-(NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
_selected = [self.evlist objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:#"eventlist" sender:self];
return indexPath;
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSString * event_id = [_selected objectForKey:#"id"];
NSString * event_name = [_selected objectForKey:#"label"];
[segue.destinationViewController setEvent_id:event_id];
[segue.destinationViewController setEvent_id:event_id];
[segue.destinationViewController setEvent_name:event_name];
}
(NextViewController is a ViewController)
- (void)viewDidLoad
{
[super viewDidLoad];
_evname_label.text = _event_name;
_evid_label.text = _event_id;
NSLog(#"This is strange %#",_event_name);
}
He logs everything as expected... the view doesn't show!
Your code appears correct. Are you sure the segue is configured correctly? Check the Style, Presentation and Transition styles.
Sorry but I had a bug in my coding... I was calling the prepareforsegue inside a for cycle and the problem was somewhere around it.
Prepareforsegue outside for and it was cool :) Thanks all
Hey there I have been working on a transit app for some time and have been stuck with this issue for a while now.
I am using iOS 5 and a storyboard. Basically I have a UITableView that displays favorite bus stop locations, when a user selects a row I use:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Favorite *favorite = [self.favoriteItems objectAtIndex:indexPath.row];
stopString = favorite.title;
routeString = favorite.subtitle;
}
With the stop and route information of the cell the user chose I then prepare for a segue that corresponds to a segue on my storyboard, pushing a detail view controller that uses the stop name and route name to display times from a plist.
I am fairly new to Objective C and iOS so I am using a segue that my friend told me would work, however, it might be the problem. The segue looks like this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destination = segue.destinationViewController;
if ([destination respondsToSelector:#selector(setDelegate:)])
{
[destination setValue:self forKey:#"delegate"];
}
if ([destination respondsToSelector:#selector(setSelection:)])
{
NSString *route = routeString;
NSDictionary *selection1 = [NSDictionary dictionaryWithObjectsAndKeys:route, #"route", stopString, #"stop", nil];
[destination setValue:selection1 forKey:#"selection"];
}
}
After the segue in my DetailViewController I grab the stop and route information in the view DidLoad:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
route = [selection objectForKey:#"route"];
stopName = [selection objectForKey:#"stop"];
NSLog(#"stopName: %#", stopName);
NSLog(#"routeName: %#", route);
}
Here is where my problems arise. When I run the simulator and click on an a cell in my table view, I am pushed to the DVC, however, the stopName and routeName are both null, so no information was sent or received. BUT, if I go back to the table and click another cell, the routeName and stopName are filled with the information that should have sent the first time I clicked a cell. If I continue this process it continues to send the information for the cell tapped previously, not currently.
So basically information is sending but only after I go through the segue twice. Obviously I want it to send the information and receive it the first time, but it is delayed and driving me nuts. I appreciate any help someone can give me as I have been searching the internet for days now trying to fix this issue, thank you so much in advance for any assistance!
prepareForSegue: is being called before didSelectRowAtIndexPath:. This is why the values you see always are lagging behind.
The better solution is to get the stopString and routeString values in your prepareForSegue: method (and not use didSelectRowForIndexPath: at all). The key to doing this is to realize that the sender parameter value being passed to prepareForSegue: is the UITableViewCell that was tapped. You can use the UITableView method indexPathForCell to get the cell's indexPath in your table, and then use that to look up the data in your favoriteItems array.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UITableViewCell *cell = (UITableViewCell*)sender;
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
Favorite *favorite = [self.favoriteItems objectAtIndex:indexPath.row];
stopString = favorite.title;
routeString = favorite.subtitle;
UIViewController *destination = segue.destinationViewController;
if ([destination respondsToSelector:#selector(setDelegate:)])
{
[destination setValue:self forKey:#"delegate"];
}
if ([destination respondsToSelector:#selector(setSelection:)])
{
NSString *route = routeString;
NSDictionary *selection1 = [NSDictionary dictionaryWithObjectsAndKeys:route, #"route", stopString, #"stop", nil];
[destination setValue:selection1 forKey:#"selection"];
}
}
Make sure that you are NOT connecting the segue to the next view controller to the tableView CELL directly. Connect to the whole UITableViewController / UIViewController (whichever you are using) and give a name, say "segueNameInStoryboard".
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Favorite *favorite = [self.favoriteItems objectAtIndex:indexPath.row];
stopString = favorite.title;
routeString = favorite.subtitle;
/* add this line */
[self performSegueWithIdentifier:#"segueNameInStoryboard" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"segueNameInStoryboard"])
{
UIViewController *nextViewController = segue.destinationViewController;
nextViewController.delegate = self;
NSDictionary *selection1 = [NSDictionary dictionaryWithObjectsAndKeys:routeString, #"route", stopString, #"stop", nil];
nextViewController.selection = selection1;
}
}
So, using storyboard you can create a segue from the UITableViewCell from the first tableViewController to a detailViewController.
Not too complicated, however, when a UISearchBarDisplayController is introduced into the storyboard mix, how can you segue the results cell to the detailViewController?
I am able to search without a problem, I followed this tutorial: http://clingingtoideas.blogspot.com/2010/02/uitableview-how-to-part-2-search.html
All I can do is select a row from the search, it turns blue and doesn't go to the detailViewController.
I have implemented the method prepareForSegue, which works for the non searched cells, but can't figure out this one.
Here's the solution that's based on the comment by #James Chen. Also using a different IndexPath depending on which state the table is in.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"toDetail"]) {
Person *person = nil;
if (self.searchDisplayController.active == YES) {
NSIndexPath *indexPath = indexPath = [self.searchDisplayController.searchResultsTableView indexPathForSelectedRow];
NSLog(#"segue - section: %d, row: %d", indexPath.section, indexPath.row);
person = [self.filteredPersonArray objectAtIndex:indexPath.row];
}
else {
NSIndexPath *indexPath = indexPath = [self.tableView indexPathForSelectedRow];
NSLog(#"segue - section: %d, row: %d", indexPath.section, indexPath.row);
person = [self.personArray objectAtIndex:indexPath.row];
}
[[segue destinationViewController] setPerson:person];
}
}
I tried your solution and found that prepareForSegue is called twice
due to the life cycle and didSelect... -> performSegueWithIdentifier.
self:prepareForSegue: object on destination controller is set
(with wrong index) because
dest:viewDidLoad: the destination controller view is loaded after which
self:didSelectRow...: the index is known and properly set.
self:prepareForSegue: object is now correct but has no side effect.
I then focused on didSelect... and came up with this solution where I deleted the segue and pushed the view programmatically:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DestTableViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:#"DestViewController"];
CustomObj *custObj = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
custObj = [filteredListContent objectAtIndex:indexPath.row];
} else {
storeToDetail = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
controller.custObj = custObj;
[self.navigationController setNavigationBarHidden:NO];
[self.navigationController pushViewController:controller animated:YES];
// presentViewController::animated:completion is always full screen (see problem below)
}
I then experienced some problems going back because I follow a segue
from a mapView, which lead to:
//DestinationViewController
- (IBAction)back:(id)sender
{
[self.navigationController popToRootViewControllerAnimated:YES]; // list
[self.presentingViewController dismissViewControllerAnimated:YES completion:nil]; // map
}
which is not the way to do it but for now it works.
However, the first part is easy and clean, and maybe it works for you too?!
Ok I think I got it, it seems like a bit of a hack but it works for my purposes:
I am using storyboard:
I have a UITableview controller with UISearchBarDisplayController directly on top of it. No code just drag and drop.
From there, I followed this tutorial to get the search bar to search correctly http://clingingtoideas.blogspot.com/2010/02/uitableview-how-to-part-2-search.html
However prepareForSegue: would only let me display a cell from the original array, not with the search array.
So I used didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (savedSearchTerm) {
searchRowSelected = indexPath.row; //<-- the trick that worked
[self performSegueWithIdentifier:#"ShowDetail" sender:self];
}
}
searchRowSelected is an int variable that I declared at the top of the class.
didSelectRowAtIndexPath: knew which row I was selecting, but prepareForSegue didn't. Thats why I needed that variable.
This is how I used it in prepareForSegue:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if ([segue.identifier isEqualToString:#"ShowDetail"]) {
dvc = [segue destinationViewController];
NSIndexPath* path = [self.tableView indexPathForSelectedRow];
int row = [path row];
if (savedSearchTerm){ //set the detailViewController with the searched data cell
myDataClass* c = [searchResults objectAtIndex:searchRowSelected];
dvc.myDataClass = c;
}else{ //set the detailViewController with the original data cell
myDataClass* c = [array objectAtIndex:row];
dvc.myDataClass = c;
}
}
}
Also use this code to clean up savedSearchTerm
-(void) searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller{
[self setSavedSearchTerm:nil];
}
If anyone has a better solution I'm all ears :)