I'm using two different custom table cells in my table view. That said, I only want didSelectRowAtIndexPath to fire when, for example, Custom Cell Identifier 2 is tapped (but not when Custom Cell Identifer 1 is tapped). How might I go about this? See code below. Right now, didSelectRowAtIndexPath fires when either cell is tapped...
- (void)viewDidLoad {
[super viewDidLoad];
static NSString *ChatTableIdentifier = #"ChatTableViewCell";
static NSString *ChatTableIdentifier2 = #"SwapDetailTableViewCell";
UINib *nib = [UINib nibWithNibName: ChatTableIdentifier bundle:nil];
[self.tableView registerNib:nib forCellReuseIdentifier: ChatTableIdentifier];
UINib *nib2 = [UINib nibWithNibName: ChatTableIdentifier2 bundle:nil];
[self.tableView registerNib:nib2 forCellReuseIdentifier: ChatTableIdentifier2];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *data = self.messages[indexPath.row];
id swaptime = data[#"swaptime"];
if ([swaptime isKindOfClass:[NSString class]]) {
static NSString *ChatTableIdentifier2 = #"SwapDetailTableViewCell";
SwapDetailTableViewCell *cell = (SwapDetailTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifier2 forIndexPath:indexPath];
NSString *time = data[#"swaptime"];
cell.startTime.text = time;
NSString *timeEnd = data[#"endswaptime"];
cell.endTime.text = timeEnd;
return cell;
} else {
static NSString *ChatTableIdentifier = #"ChatTableViewCell";
ChatTableViewCell *cell = (ChatTableViewCell *)[tableView dequeueReusableCellWithIdentifier:ChatTableIdentifier forIndexPath:indexPath];
NSString *userName = data[#"name"];
cell.sendingUser.text = userName;
NSString *messageBody = data[#"body"];
cell.messageDisplayed.text = messageBody;
NSString *timeReceived = data[#"published at"];
cell.timeStamp.text = timeReceived;
return cell;
}
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SwapDetailsViewController *detailViewController = [[SwapDetailsViewController alloc]
initWithNibName:#"SwapDetailsViewController" bundle:nil];
detailViewController.swapDetails = [self.messages objectAtIndex:indexPath.row];
[self presentViewController:detailViewController animated:YES completion:nil];
}
You can put an if condition inside didSelectRowAtIndexPath as the one you put in cellForRowAtIndexPath
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *data = self.messages[indexPath.row];
id swaptime = data[#"swaptime"];
//perform action based on this, and don't do anything in second case
if ([swaptime isKindOfClass:[NSString class]])
//this indexPath will always contain cell of one kind
}else{
// this indexPath will contain second cell kind
}
}
Or alternatively you can also do-
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
// this will give you cell at the selected indexPath
id cell = [tableView cellForRowAtIndexPath:indexPath]
//perform action based on this, and don't do anything in second case
if ([cell isKindOfClass:[SwapDetailTableViewCell class]])
}else{
}
}
If you don't want a particular row to be selected, the proper solution is to implement the tableView:willSelectRowAtIndexPath: delegate method and return nil for rows you don't want selected.
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (some condition) {
return nil;
} else {
return indexPath;
}
}
So now you need to determine the proper condition. I can think of three ways in your case:
Use the same if condition you have in cellForRowAtIndexPath:.
Get the row's cell and look at the cell's class.
Get the row's cell and look at the cell's reuse identifier.
Option 1 isn't ideal because if you change the logic in cellForRowAtIndexPath you have to remember to change it here too. Options 2 and 3 are essentially the same in this case. I'd go with option 3.
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if ([cell.reuseIdentifier isEqualToString: ChatTableIdentifier) {
return nil;
} else {
return indexPath;
}
}
Since you are now using ChatTableIdentifier in three places, I would move the lines:
static NSString *ChatTableIdentifier = #"ChatTableViewCell";
static NSString *ChatTableIdentifier2 = #"SwapDetailTableViewCell";
to before the #implementation line and remove them from everywhere else in the file. No sense in recreating the same variables over and over.
In didSelectRowAtIndexPath you can call method
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
for the indexPath of a current row and get the cell you are currently selecting. Every cell has a reuse identifier, so you can check the identifier of the cell you are selecting. if it equals "SwapDetailTableViewCell", then you can perform everything you need(for example move to another viewController)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
if ([cell.reuseIdentifier isEqual: #"SwapDetailTableViewCell"]) {
SwapDetailsViewController *detailViewController = [[SwapDetailsViewController alloc]
initWithNibName:#"SwapDetailsViewController" bundle:nil];
detailViewController.swapDetails = [self.messages objectAtIndex:indexPath.row];
[self presentViewController:detailViewController animated:YES completion:nil];
}
Related
I have a UITableView that holds a tableView that populates 'prototype cells' from a mutable array and whose cells show an 'accessory checkmark' when you select them. I have a textfield in a view below the tableview and whose data I am then appending to the array that is populating the tableview. My problem is that after I append the new data which adds a cell to the tableview I have to touch a cell twice in order to deselect any of the cells I previously selected.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"skillName" forIndexPath:indexPath];
cell.textLabel.text = skillsOptions[indexPath.row];
// Configure the cell...
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *tableViewCell = [tableView cellForRowAtIndexPath:indexPath];
//tableViewCell.accessoryView.hidden = NO;
tableViewCell.accessoryType = UITableViewCellAccessoryCheckmark;
}
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *tableViewCell = [tableView cellForRowAtIndexPath:indexPath];
//tableViewCell.accessoryView.hidden = YES;
tableViewCell.accessoryType = UITableViewCellAccessoryNone;
[skillsList removeObject:skillsOptions[indexPath.row]];
}
-(void)grabSelectedSkills {
for (NSIndexPath *indexPath in self.tableView.indexPathsForSelectedRows) {
NSString *skill = skillsOptions[indexPath.row];
//NSString *skill = [NSString stringWithFormat:#"%li",(long)indexPath.row];
[skillsList addObject:skill];
}
NSLog(#"skillsList: %#",skillsList);
}
- (IBAction)continue:(id)sender {
[self grabSelectedSkills];
}
- (IBAction)addOtherSkills:(id)sender {
if (self.otherSkill.text.length > 1) {
[skillsOptions addObject:self.otherSkill.text];
self.otherSkill.text = #"";
[self.tableView endEditing:YES];
[self.tableView reloadData];
}
You need to track your selection/deselection status outside of the cell - a dictionary keyed by the "skill option" is probably a good choice.
You can then use this in cellForRowAtIndexPath and didSelectRowAtIndexPath to add/remove the check mark as required. You should deselect the row in didSelectRowAtIndexPath and get rid of didDeselectRowAtIndexPath -
Create self.selectedCells as an NSMutableDictionary property.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"skillName" forIndexPath:indexPath];
// Configure the cell...
NSString *skillOption=skillsOptions[indexPath.row];
cell.textLabel.text = skillOption;
if (self.selectedCells[skillOption]!= nil) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *tableViewCell = [tableView cellForRowAtIndexPath:indexPath];
NSString *skillOption=skillsOptions[indexPath.row];
if (self.selectedCells[skillOption]!= nil) {
tableViewCell.accessoryType = UITableViewCellAccessoryNone;
[self.selectedCells removeObjectForKey:skillOption];
} else {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
self.selectedCells[skillOption]=skillOption;
}
[tableview deselectRowAtIndexPath:indexPath];
}
I think you should only use didSelectRowAtIndexPath: method (drop the didDeselectRowAtIndexPath: method) and check to select the cell if it's not and vice versa as-
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
BOOL cellSelected = NO;
// set cellSelected based on your condition
if(cellSelected)
{
// code to deselect the cell
}
else
{
// code to select the cell
UITableViewCell *tableViewCell = [tableView cellForRowAtIndexPath:indexPath];
//tableViewCell.accessoryView.hidden = NO;
tableViewCell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
I am new to iOS development, i've been following apple intro to iOS and implemented the to-do list app.
I have tableview that shows the list of the current to-do list, and another view controller that allows the user to add a new item in a textfield then add to the list.
I have a little + on top of my tableview that performs a segue action to the add to-do item view.
I want to be able to perform the same action if the user taps on an empty cell
I tried to put this code in and it works but I want to put it in the right position so it only triggers when the user hits empty space in the table:
[self performSegueWithIdentifier:#"showAddItem" sender:self];
Thank you.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"ListPrototypeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// Configure the cell...
ABCTodoItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
cell.textLabel.text = toDoItem.itemName;
if (toDoItem.completed) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
else
{
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
In my opinion this solution is the cleanest:
Add a tap gesture to your UITableView's backgroundView. If the backgroundView is nil, add a transparent UIView, and add a gesture to it.
Now hook up your action to this gesture recognizer.
I can see at least two solutions here.
Solution 1. Adding en empty cell by modifying the data source
After you have populated your toDoItems add this:
[self.toDoItems addObject:#""];
Notice that toDoItems should be type of NSMutableArray.
Now let's handle creating cells:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"ListPrototypeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
NSObject *objItem = [self.toDoItems objectAtIndex:indexPath.row];
if([objItem isKindOfClass: [ABCTodoItem class]]) {
ABCTodoItem *toDoItem = (ABCTodoItem*) objItem;
cell.textLabel.text = toDoItem.itemName;
}
else if([objItem isKindOfClass: [NSString class]]) {
NSString *item = (NSString*) objItem;
cell.textLabel.text = item;
}
return cell;
}
Touch action:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSObject *objItem = [self.toDoItems objectAtIndex:indexPath.row];
if([objItem isKindOfClass: [ABCTodoItem class]]) {
ABCTodoItem *toDoItem = (ABCTodoItem*) objItem;
// Open your toDoItem
}
else if([objItem isKindOfClass: [NSString class]]) {
self performSegueWithIdentifier:#"showAddItem" sender:self];
}
}
Solution 2. Adding an empty cell without modifying the data source
This makes it possible to create one extra cell:
- (NSInteger)numberOfRowsInSection:(NSInteger)section {
return [self.toDoItems count] + 1;
}
Now again let's handle creating cells:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = #"ListPrototypeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
if([indexPath.row < [self.toDoItems count]) {
NSObject *objItem = [self.toDoItems objectAtIndex:indexPath.row];
if([objItem isKindOfClass: [ABCTodoItem class]]) {
ABCTodoItem *toDoItem = (ABCTodoItem*) objItem;
cell.textLabel.text = toDoItem.itemName;
}
}
else {
cell.textLabel.text = #"";
}
return cell;
}
And then handling the touch action:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if([indexPath.row < [self.toDoItems count]) {
NSObject *objItem = [self.toDoItems objectAtIndex:indexPath.row];
if([objItem isKindOfClass: [ABCTodoItem class]]) {
ABCTodoItem *toDoItem = (ABCTodoItem*) objItem;
// Open your toDoItem
}
}
else {
self performSegueWithIdentifier:#"showAddItem" sender:self];
}
}
I hope I have understood your question correctly; correct me if I am wrong. You have a to-do List populated in a TableViewController which has some tasks and one (or more) empty cells. When you click on these cells, you would want to call [self performSegueWithIdentifier:#"showAddItem" sender:self]; a method that presents a ViewController that allows you to add more items into the list.
Once you have populated your TableViewController in such a way that you have an empty cell, you can always get the NSIndexPath of the empty cell.
When you tap on a cell, a method will be called, called: (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath. As you can see, you will get the index information of the tapped cell. There, you can add the code like so:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Add your logic for the rest of the items
//If you have 7 items in the to-do list, you would have 8 cell,
//the eight one being empty. If indexPath.row is equal to 8 then,
//it says that it MUST be the last item, that is empty
if(indexPath.row == self.itemsInTheList.count) {
[self performSegueWithIdentifier:#"showAddItem" sender:self];
}
}
Alternatively, you can set the tag property of the cell; and that's the story for a different day.
I have UITableView with prototype cell.I have a label in cells with various values(text value).
when add a new row, some of labels in new rows created with previous cell values not default values:
What can i do?
Is my question clear?
This is my code:
- (void)viewDidLoad
{
[super viewDidLoad];
gamers = [[NSMutableArray alloc] initWithObjects:#"Player1",#"Player2", nil];
}
- (IBAction)btnAddRow:(id)sender {
[gamers addObject:#"new player"];
[_tableView reloadData];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *tableIdentifier = #"gamerCell";
nTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tableIdentifier];
if (cell == nil) {
cell = [[nTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:tableIdentifier];
}
cell.lblTitle.text = [gamers objectAtIndex:indexPath.row];
return cell;
}
Please find the updated project in the link
UITableView-dynamic-row-buttons
Hope this helps.
Where do you update the score? if you are not doing, make, according to your logic, and then set this value in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *tableIdentifier = #"gamerCell";
nTableViewCell *cell = (nTableViewCell *)[tableView dequeueReusableCellWithIdentifier:tableIdentifier];
if (cell == nil) {
cell = [[nTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:tableIdentifier];
}
cell.lblTitle.text = [gamers objectAtIndex:indexPath.row];
cell.lblScore.text = [NSString stringWithFormat:#"%i", (int)scoreProperty];
return cell;
}
I'm unable to populate the second UITableView Controller, wondering if anyone could help.
I'm using a websites API, JSON, and RestKit for the data. I believe that part is working fine because my first VC loads fine.
But I'm not sure if I need to use prepareForSegue and/or didSelectRowAtIndexPath so that I can identify the cell/row selected in the first VC, so that the second VC populates with the (correct) data.
1st VC populates correct:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return sports.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
Sport *sport = [sports objectAtIndex:section];
return sport.leagues.count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
Sport *sport = [sports objectAtIndex:section];
return sport.name;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"standardCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Sport *sport = [sports objectAtIndex:indexPath.section];
League *league = [sport.leagues objectAtIndex:indexPath.row];
cell.textLabel.text = league.name;
return cell;
}
2nd VC populates blank table:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return leagues.count;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
League *league = [leagues objectAtIndex:section];
return league.teams.count;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
League *league = [leagues objectAtIndex:section];
return league.name;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"standardCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
League *league = [leagues objectAtIndex:indexPath.section];
Team *team = [league.teams objectAtIndex:indexPath.row];
cell.textLabel.text = team.name;
return cell;
}
Or if I try to add extra code for 1st VC, app crashes before getting to 2nd VC:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
TeamsViewController *teamsViewController = [[TeamsViewController alloc] initWithNibName:#"TeamsViewController" bundle:nil];
teamsViewController.title = [[sports objectAtIndex:indexPath.row] objectForKey:#"sports"];
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"leagueDetail"]) {
TeamsViewController *tvc = [segue destinationViewController];
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
tvc.data = [self.navigationController objectInListAtIndex:indexPath.row]
}
Would really appreciate any help!
I am a little bit confused with excepting of creation UITableViewCell object. When you ask table view for dequeuing cell it returns one which it does not need at the moment, if there are no unused cell you have to create a new one. Try code like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// Create cell
NSString *cellIdentifier = #"cell";
UITableViewCell *cell = nil;
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:cellIdentifier];
cell.selectionStyle = UITableViewCellSelectionStyleGray;
}
cell.textLabel.text = [NSString stringWithFormat:#"cell %d", indexPath.row];
return cell;
}
I have a table view that list currencies ... I would like that only one can be selected at one time (if the user clickes an already selected cell it should remain selected)... the check mark is an uiimageview...
Here is the code:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[self.currencyList fetchedObjects] count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CurrencyCell";
CurrencyCell *cell = (CurrencyCell *)[self.tableView dequeueReusableCellWithIdentifier:CellIdentifier];
Currency *currency = [self.currencyList objectAtIndexPath:indexPath];
cell.currencyName.text = currency.name;
cell.curencyAbreviation.text = currency.abreviation;
if ([currency.isDefault boolValue] == YES) {
cell.selectedCurrency.image = [UIImage imageNamed:#"Checkmark.png"];
checkedCell = indexPath;
NSLog(#"The default currency cellfor row is :%#",currency);
NSLog(#"the checkedcell index path is:%#",checkedCell);
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// [self.delegate theSelectedCurrencyIs:[self.currencyList objectAtIndexPath:indexPath]];
if (![indexPath isEqual:checkedCell]) {
CurrencyCell *cell = (CurrencyCell *)[self.tableView cellForRowAtIndexPath:indexPath];
cell.selectedCurrency.image = [UIImage imageNamed:#"Checkmark.png"];
selectedCurrency = [self.currencyList objectAtIndexPath:indexPath];
NSLog(#"The selected currency is %#",selectedCurrency);
NSLog(#"The indexpath of the new selected currency is %#",indexPath);
CurrencyCell *previoslySelectedCell = (CurrencyCell *)[self.tableView cellForRowAtIndexPath:checkedCell];
NSLog(#"name of the previously selected cell %#",previoslySelectedCell.currencyName);
previoslySelectedCell.selectedCurrency.image = nil;
// [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObjects:indexPath,checkedCell, nil] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView reloadData];
checkedCell = indexPath;
}
}
The problem is that the check-mark image is showing up in more than one selected cell.. the selected indexpath is ok... but a lot of cells look check-marked ... seems like a few on every scrolled screen
What would be the problem here?