I just recently noticed this issue in my app, and it seems to be an anomaly because I cannot pin down how/why it is happening, and there is no way to predict when. I have a custom UITableViewCell that I am loading into my UITableView subclass. I only have 5 cells in my tableView at all times, they are static and not prototypes. The issue I'm having is that sometime after I dismiss a modal view that I got to my selecting one of the cells, other cells become buggy. Here is what happens:
•Either I touch the cell and it performs the segue as planned
•About 10+ seconds after I touch the cell the segue is performed
•I touch the cell and nothing happens, but if I touch anywhere else on the view, the segue is performed
It is very very similar to the problem posted "Here" as well.
Here is my code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
//[super tableView:tableView didSelectRowAtIndexPath:indexPath];
//self.selectedObject = [self.objects objectAtIndex:indexPath.row];
NSLog(#"I DID SELECT path %ld", (long)indexPath.row);
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
if(indexPath.row == 0 && self.matchOneSetNum <= 3)
{
self.whereFrom = 1;
[self performSegueWithIdentifier:#"enterScoresSegue" sender:self];
}
if(indexPath.row == 1 && self.matchTwoSetNum <= 3)
{
self.whereFrom = 2;
[self performSegueWithIdentifier:#"enterScoresSegue" sender:self];
}
if(indexPath.row == 2 && self.matchThreeSetNum <= 3)
{
self.whereFrom = 3;
[self performSegueWithIdentifier:#"enterScoresSegue" sender:self];
}
if(indexPath.row == 3 && self.matchFourSetNum <= 3)
{
self.whereFrom = 4;
[self performSegueWithIdentifier:#"enterScoresSegue" sender:self];
}
if(indexPath.row == 4 && self.matchFiveSetNum <= 3)
{
self.whereFrom = 5;
[self performSegueWithIdentifier:#"enterScoresSegue" sender:self];
}
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"enterScoresSegue"])
{
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
UINavigationController *newView = (UINavigationController *)[segue destinationViewController];
EnterScoresViewController *enter = newView.topViewController;
enter.myDelegate = self;
enter.otherTeam = self.otherTeam;
if(self.whereFrom == 1)
{
enter.match = self.matchOne;
enter.setNumber = self.matchOneSetNum;
enter.matchOnePlayer = self.playerName;
}
if(self.whereFrom == 2)
{
enter.match = self.matchTwo;
enter.setNumber = self.matchTwoSetNum;
enter.matchTwoPlayer = self.playerNameTwo;
}
if(self.whereFrom == 3)
{
enter.match = self.matchThree;
enter.setNumber = self.matchThreeSetNum;
enter.matchThreePlayer = self.playerNameThree;
}
if(self.whereFrom == 4)
{
enter.match = self.matchFour;
enter.setNumber = self.matchFourSetNum;
enter.matchFourPlayers = self.playerStringsFour;
}
if(self.whereFrom == 5)
{
enter.match = self.matchFive;
enter.setNumber = self.matchFiveSetNum;
enter.matchFivePlayers = self.playerStringsFive;
}
}
}
There was an answer on a similar question that fixed the issue for me, here is the link: https://stackoverflow.com/a/23747207/2584268
And here is the full text:
After filling a Radar Apple responded with adding:
dispatch_async(dispatch_get_main_queue(), ^{});
To the end of the following method, which does the trick:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
In the method that presents the controller, you can schedule a no-op
block which will force the runloop to spin and there will be no delay.
Radar Ref: 15196237, feel free to file another and reference this.
wendrring if you segue in storyboard from a cell or from the ViewController(sender is self in this case), if from a cell the sender should be a cell too.
[self performSegueWithIdentifier:#"enterScoresSegue" sender:[self.tableView cellForRowAtIndexPath:indexPath]];
Related
Here is what I go so far:
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if (preivousViewController isEqualToString:#"...")
{
if ([self.filteredArray count] == 0)
self.person = self.users[indexPath.row];
else
self.person = self.filteredArray[indexPath.row];
self.mugshot = cell.imageView.image;
[self performSegueWithIdentifier:#"SelectPerson" sender:self];
} else {
if ([self.filteredArray count] == 0)
self.person = self.users[indexPath.row];
else
self.person = self.filteredArray[indexPath.row];
self.mugshot = cell.imageView.image;
[self performSegueWithIdentifier:#"selectVisitee" sender:self];
}
}
It looks messy, that's why im trying to fix it up (that previousViewController part is just made up to show you what I want to try to do).
What im trying to say is: If how you got to this view was via this segue, or from that view controller with the name suchandsuch, then preform this segue or that segue. Is there a way I can do that?
You can check the previous ViewController of the stack from the Navigation Controller.
- (void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if ([self.filteredArray count] == 0)
self.person = self.users[indexPath.row];
else
self.person = self.filteredArray[indexPath.row];
self.mugshot = cell.imageView.image;
//Not the viewcontroller name string. Use the ViewController class name
if ([self backViewController] == YOURVIEWCONTROLLER)
{
[self performSegueWithIdentifier:#"SelectPerson" sender:self];
} else {
[self performSegueWithIdentifier:#"selectVisitee" sender:self];
}
}
Use this below method,
- (UIViewController *)backViewController
{
NSInteger numberOfViewControllers = self.navigationController.viewControllers.count;
if (numberOfViewControllers < 2)
return nil;
else
return [self.navigationController.viewControllers objectAtIndex:numberOfViewControllers - 2];
}
When I click a table cell in my application it opens two view controllers. Which it seems to assume that I have clicked two cells, since it opens the cell before and the one I have clicked.
The first 3 cells are pretty much static, every other cell underneath the "ingredients" heading are dynamic. Hence why indexPath - 3
I created the segue between the table view and the view controller in the interface builder.
Table View:
First view controller/segue:
Second view controller segue:
Code for the segue:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
self.selectedIngredient = self.selectedProduct.ingredientsArray[indexPath.row - 3];
[self performSegueWithIdentifier:#"ingViewSegue" sender:self];
NSLog(#"selected ingredient %#", self.selectedIngredient);
}
#pragma mark segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
IngredientWebViewController *webView = segue.destinationViewController;
webView.selectedIngredient = self.selectedIngredient;
}
Number of rows in table:
#pragma mark - Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_selectedProduct.ingredientsArray count] + 3;
}
Code to populate table:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == 0)
{
TitleCell* titleCell = (TitleCell*)[self.tableView dequeueReusableCellWithIdentifier:#"TitleCell" forIndexPath:indexPath];
titleCell.nameLabel.text = self.selectedProduct.productName;
titleCell.brandLabel.text = self.selectedProduct.productBrand;
return titleCell;
}
else if(indexPath.row == 1)
{
return [self.tableView dequeueReusableCellWithIdentifier:#"RatingCell" forIndexPath:indexPath];
}
else if(indexPath.row == 2)
{
return [self.tableView dequeueReusableCellWithIdentifier:#"IngredientHeaderCell" forIndexPath:indexPath];
}
else
{
IngredientsCell* ingCell = (IngredientsCell*)[self.tableView dequeueReusableCellWithIdentifier:#"IngredientCell" forIndexPath:indexPath];
ingCell.ingredientNameLabel.text = self.selectedProduct.ingredientsArray[indexPath.row - 3];
if([self.selectedProduct.ratingsArray[indexPath.row -3] isEqual: #"4"])
{
ingCell.ingredientsImage.image = [UIImage imageNamed:#"icon_safe.png"];
}
else if([self.selectedProduct.ratingsArray[indexPath.row -3] isEqual: #"3"])
{
ingCell.ingredientsImage.image = [UIImage imageNamed:#"icon_low.png"];
}
else if([self.selectedProduct.ratingsArray[indexPath.row -3] isEqual: #"2"])
{
ingCell.ingredientsImage.image = [UIImage imageNamed:#"icon_med.png"];
}
else if([self.selectedProduct.ratingsArray[indexPath.row -3] isEqual: #"1"])
{
ingCell.ingredientsImage.image = [UIImage imageNamed:#"icon_high.png"];
}
else
{
ingCell.ingredientsImage.image = [UIImage imageNamed:#"icon_safe.png"];
}
return ingCell;
}
}
Code to alter cell height:
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if(indexPath.row == 0)
{
UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:#"TitleCell"];
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height + 1;
}
else if(indexPath.row == 1)
{
UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:#"RatingCell"];
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return 118;
}
else if(indexPath.row == 2)
{
UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:#"IngredientHeaderCell"];
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return size.height + 1;
}
else
{
UITableViewCell* cell = [self.tableView dequeueReusableCellWithIdentifier:#"IngredientCell"];
CGSize size = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
return 60;
}
}
A segue will be performed automatically while a cell is tapped. Try the following code:
// - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
// {
// self.selectedIngredient = self.selectedProduct.ingredientsArray[indexPath.row - 3];
// [self performSegueWithIdentifier:#"ingViewSegue" sender:self];
// NSLog(#"selected ingredient %#", self.selectedIngredient);
// }
#pragma mark segue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
IngredientWebViewController *webView = segue.destinationViewController;
webView.selectedIngredient = self.selectedProduct.ingredientsArray[indexPath.row - 3];
}
I have a problem with using SWRevealViewController. How to call prepare for segue in didSelectRowAtIndexPath? It is necessary because in tableView I have name of categories and some of this categories have its own subcategories. For example my list:
- category1
- category2
+ category3
- category4
So, when I click on + category3 I should see
- category1
- category2
+ category3
- subcategory1
- subcategory2
- category4
But I could not to do this because of prepareForSegue
Here is my code:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section <= _categories.count) {
GDCategory *category = [[_subCategories objectAtIndex:indexPath.section] objectAtIndex:indexPath.row];
_gdCategory = category;
if (category.subcategories.count > 0) {
NSNumber *categoryId = #(category.dbId);
if ([_selectedSubCategories containsObject:categoryId]) {
[_selectedSubCategories removeObject:categoryId];
} else {
[_selectedSubCategories addObject:categoryId];
}
NSLog(#"There are %lu subcategories in %#", (unsigned long)category.subcategories.count, category.name);
[self recalcSubcategories];
[tableView reloadData];
} else {
//Here I should call prepareForSegue
}
NSLog(#"selected category is %#", category.name);
}
}
- (void)prepareForSegue: (UIStoryboardSegue *)segue sender:(id)sender {
// configure the destination view controller:
if ( [sender isKindOfClass:[UITableViewCell class]] ) {
UINavigationController *navController = segue.destinationViewController;
GDCategoryVC *categoryVC = [navController childViewControllers].firstObject;
if ( [categoryVC isKindOfClass:[GDCategoryVC class]] ) {
categoryVC.selectedCategory = _gdCategory;
}
}
}
Also, I could not to pass data throw VCs, because prepareForSegue call firstly. Please help me. Thanks!
//in prepareForSegue method
NSIndexPath *indexPath = [tableView indexPathForSelectedRow];
and you can get selected row value by using indexPath.row
To change the current View you shouldn't call prepareForSegue. Use performSegueWithIdentifier instead. PrepareForSegue will be called automatically.
If you want to pass the UITableViewCell as sender you need to call the cellForRowAtIndexPath from your TableViewDataSource
else {
//Here I should call prepareForSegue
UITableViewCell *cell = [self tableView:tableView cellForRowAtIndexPath:indexPath] //call self if dataSource is implement in your class
[self performSegueWithIdentifier:segueIdentifier sender:cell] // Insert your identifier for the wished Segue
}
With SWRevealController it can happen that the View did changed like it should, but the RevealMenu is still visible. For that issue you can call:
[self.revealViewController setFrontViewPosition:FrontViewPositionLeft animated:YES];
I have a tableview controller and I would like to override the segue for the final row. Instead of going to the standard destination view controller I want to send it to another controller. How do I do this?
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *myIndexPath = [self.tableView
indexPathForSelectedRow];
long row = [myIndexPath row];
if ([[segue identifier] isEqualToString:#"ShowLocationsTableView"])
{
LocationsTableViewController *ViewController =
[segue destinationViewController];
ViewController.categoryDetailModel = #[_categoryTitle[row],
_categoryImages[row]];
}
}
This is the current prepareForSegue and I would like to change it. I would like to send it to another view controller using a segue "aboutCat". How do I do this?
I don't understand what happens between prepareForSegue [current] and viewDidLoad [destination]. Thanks
Sideeffects
Initial problem was solved by 'Nikita Took' yet there are some side effects
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
NSIndexPath *myIndexPath = [self.tableView indexPathForSelectedRow];
long row = [myIndexPath row];
if ([_categoryTitle[row] isEqualToString:#"About"])
{
NSLog(#"SKIPPING PREPARE");
}
else if ([[segue identifier] isEqualToString:#"ShowLocationsTableView"])
{
LocationsTableViewController *ViewController =
[segue destinationViewController];
ViewController.categoryDetailModel = #[_categoryTitle[row],
_categoryImages[row]];
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.tableView)
{
// I assume you have 1 section
NSLog(#"DIDSELECTROW");
NSLog(#"INDEX PATH %i", indexPath.row + 1);
NSLog(#"%i", [tableView numberOfRowsInSection:0]);
if (indexPath.row + 1 == [tableView numberOfRowsInSection:0])
{
NSLog(#"ABOUT");
[self performSegueWithIdentifier:#"aboutCat" sender:self];
}
else
{
NSLog(#"ELSE");
[self performSegueWithIdentifier:#"ShowLocationsTableView" sender:self];
}
}
}
Now the about works!!! But if I click on another row which is one of the mainSegue. It will call the subview controller 2x. I can verify this with NSLOG statements in the detailview viewdidload. The issue is that now the navigation controller takes two steps to return home.
[home] -> [detail view] -> [same detail view]
I can verify that the didselectrowatindexpath happens before the prepareforsegue
Any ideas why two segues are preformed?
Assume you have two segues: MainSegue (for all rows except the last one) and LastRowSegue (for the last row) you can do:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == self.tableView) {
// I assume you have 1 section
if (indexPath.row == [tableView numberOfRowsInSection:0]) {
[self performSegueWithIdentifier:#"LastRowSegue" sender:self];
} else {
[self performSegueWithIdentifier:#"MainSegue" sender:indexPath];
}
}
}
Try this for prepareForSegue. If it's not what you need, please explain what do you mean
It works but I'm getting double layered table views
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"MainSegue"]) {
NSIndexPath *selectedIndexPath = sender;
LocationsTableViewController *locationController = segue.destinationViewController;
locationController..=categoryDetailModel = #[_categoryTitle[selectedIndexPath.row], _categoryImages[selectedIndexPath.row]];
}
}
You can't do this in prepareForSegue -- the source and destination view controllers are already defined by the time prepareForSegue is called. You need to make two segues from your controller (not the cell) and call performSegueWithIdentifier:sender" in didSelectRowAtIndexPath. Choose which segue to perform based on the indexPath.
You can do something like this
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int lastIndex=[tableArray count]-1;
if (indexPath.row == lastIndex) {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
NSString* viewType = #"MenuVC";//u can have the identifier of the LocationViewCOntroller or where ever you wanna go from here
UIViewController* viewController = [storyboard instantiateViewControllerWithIdentifier:viewType];
self.navigationController.viewControllers = [NSArray arrayWithObjects:viewController, nil];
} else {
[self performSegueWithIdentifier:#"usualSegue" sender:self];
}
}
I have a table view with rows X,Y,Z.What I want is when user select X it should show me another child table view with rows X1,X2,X3; when user select Y its should load another child table view with rows Y1,Y2,Y3 and so for Z.
Here is what I'm doing:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
arrOEMs = [[NSMutableArray alloc]init];
OEMs = [NSArray arrayWithObjects:#"X",#"Y",#"Z", nil];
//NSDictionary *oemDict = [NSDictionary dictionaryWithObject:OEMs forKey:#"OEMs"];
//[arrOEMs addObject:oemDict];
childrenof1stOEM = [NSArray arrayWithObjects:#"X1",#"X2",#"X3", nil];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == 0)
{
NSLog(#"1st selected");
//show X1,X2,X3 in another table view
passIndexPath = indexPath;
//NSDictionary *dictinary = [arrOEMs objectAtIndex:indexPath.section];
//NSArray *array = [dictinary objectForKey:#"OEMs"];
//selectedOEMs = [array objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:#"OEMDetailsConnectorSegue" sender:self];
}
if (indexPath.row == 1) {
NSLog(#"2 selected");
//Show Y1,Y2,Y3 in another table view
}
if (indexPath.row == 2) {
NSLog(#"3 selected");
}
Calling segue like following:
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"OEMDetailsConnectorSegue"])
{
//NSLog(#"2 selected");
OEMsDetailViewController *destViewController = segue.destinationViewController;
//destViewController.OemNames = self.selectedOEMs; //showing exception at this step
//destViewController.parentIndex = passIndexPath;
if (passIndexPath.row == 0) {
destViewController.childData = [childrenof1stOEM objectAtIndex:passIndexPath.row];
}
}
}
"childrenof1stOEM " is not getting assigned to "childData"...Also
how can I assign "childData" to next table view cell.It seems that I have not assigned so throwing exception, what I'm doing wrong?Could any one plaese modify my code to make it work or provide me some link??