I want to change the color and the text of the button when UITableViewCellEditingStyleDelete, how can I do that? Here's how I call the method for deleting a cell :) Thanks!
(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
if(editingStyle == UITableViewCellEditingStyleDelete){
[self.tasks removeObjectAtIndex:indexPath.row];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:self.tasks forKey:#"tasks"];
[tableView beginUpdates];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationRight];
[tableView endUpdates];
}
}
What you can do is the following:
-(NSString *)tableView:(UITableView *)tableView titleForDeleteConfirmationButtonForRowAtIndexPath:(NSIndexPath *)indexPath
{
//Set NSString for button display text here.
NSString *newTitle = #"Remove";
return newTitle;
}
The method call to accomplish this is found in the Apple iOS Dev Docs here
Honestly, I would NOT change the color - the Apple HIG (Human Interface Guidelines) would likely recommend the color stay a consistent red to make sure users don't have a different expectation for your app's behavior.
You can custom your cell. And then, add UISwipeGestureRecognizer for this cell on method cellforRowatindexPath like this:
//custom delete button on cell comment
UISwipeGestureRecognizer * swipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(cellWasSwiped:)];
[swipeRecognizer setDirection:(UISwipeGestureRecognizerDirectionLeft |
UISwipeGestureRecognizerDirectionRight)];
[cell addGestureRecognizer:swipeRecognizer];
Implement cellWasSwiped function:
- (void)cellWasSwiped:(UISwipeGestureRecognizer *)recognizer {
//edit your custom button delete in here.
.....
[self.tableView commitEditingStyle:UITableViewCellEditingStyleDelete forRowAtIndexPath:indexPath];
}
Related
I need idea how to implement this kind of custom UITableViewCell by animating, just like the out-of-the-box feature of UITableViewCell when sliding left or right.
What I've done so far (and is working), is:
Add a button with that circle image.
Toggle visibility of that button.
Toggle highlighted and normal state of the button.
Toggle constraints of the whole views when showing or hiding the radio button in #2.
Reload Data.
I'm thinking that this question might make my implementation cleaner when instead of toggling visibility and constraints of my custom tableViewCell, just slide it or animate it.
EDIT: These radio buttons appear only in a certain mode, like editing mode. So normally, there are no radio buttons in my cells.
You can try this I created Simple Demo.
ViewController.h
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController<UITableViewDelegate,UITableViewDataSource>
{
NSMutableArray *dataArray;
}
#property (weak, nonatomic) IBOutlet UITableView *mTableView;
// You can toggle the Selection by Means you can show hide the checkboxes
- (IBAction)didTapBringCheckBoxBtn:(id)sender;
#end
View controller.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
dataArray=[[NSMutableArray alloc]init];
[dataArray addObject:#"Apple"];
[dataArray addObject:#"Mango"];
[dataArray addObject:#"Papaya"];
[dataArray addObject:#"Guava"];
[dataArray addObject:#"Pineapple"];
[dataArray addObject:#"Strawberry"];
[dataArray addObject:#"Banana"];
[dataArray addObject:#"Grapes"];
[dataArray addObject:#"Pomegranate"];
[dataArray addObject:#"Green Tea"];
[dataArray addObject:#"Raisin"];
self.mTableView.delegate=(id)self;
self.mTableView.dataSource=(id)self;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#pragma mark - Check Box Button Action
- (IBAction)didTapBringCheckBoxBtn:(id)sender {
if(self.mTableView.editing==YES)
{
[self.mTableView setEditing:NO animated:YES];
}else{
[self.mTableView setEditing:YES animated:YES];
}
}
#pragma mark - UITableView DataSource Methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [dataArray count];
}
- (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];
}
cell.textLabel.text = [dataArray objectAtIndex:indexPath.row];
return cell;
}
#pragma mark - UITableView Delegate Methods
- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 3;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"user selected %#",[dataArray objectAtIndex:indexPath.row]);
}
- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"user de-selected %#",[dataArray objectAtIndex:indexPath.row]);
}
#end
OutPut Will look Like This :
At Start Not showing Checkboxes :
On click Show CheckBox :
You did the harder, you just need to hide your button, here is a similar implementation I did to have a delete button and delete it without a confirmation.
1) Made a subview in content view, and made it bigger than content view with a negative leading space (-42 is the value of the slide offset)
Then put your checkmark button in this hidden space like I did for mine:
Then when you will set editing mode to true, your checkbox will appear with slide animation ;)
Lets consider your data source looks like
NSMutableDictionary *sampleRecord = [[NSMutableDictionary alloc] initWithObjectsAndKeys:#"isSelected",#"true",#"title",#"title text", nil];
records = [[NSMutableArray alloc] init];
[records addObject:[sampleRecord copy]];
[records addObject:[sampleRecord copy]];
[records addObject:[sampleRecord copy]];
And your table delegate/data source methods should be
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return [records count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//Initialize your cell
//Read the all views from your cell
//Now add the click event to your radio button (UIButton)
[myButton addTarget:self action:#selector(changeRadioState) forControlEvents: UIControlEventTouchUpInside];
return cell;
}
- (IBAction)changeRadioState:(id)sender
{
//Detect the row index fro table where is the button present
CGPoint buttonPosition = [sender convertPoint:CGPointZero toView:tableView];
NSIndexPath *indexPath = [tableView indexPathForRowAtPoint:buttonPosition];
//Now get the resource data for clicked row
NSMutableDictionary *record = [records objectAtIndex:indexPath.row];
//Change the selected state images
if ([[records valueForKey:#"isSelected"] isEqualToString:#"true"]) {
[sender setImage:[UIImage imageNamed:#"unSelectStateImage"] forState:UIControlStateNormal];
[record setValue:#"false" forKey:#"isSelected"];
}
else{
[sender setImage:[UIImage imageNamed:#"selectedStateImage"] forState:UIControlStateNormal];
[record setValue:#"true" forKey:#"isSelected"];
}
//Reload entire table
[tableView reloadData];
//Or else you can reload the single row by
[tableView beginUpdates];
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
MGSwipeTableCell is an easy to use UITableViewCell subclass that allows to display swipeable buttons with a variety of transitions.
https://github.com/MortimerGoro/MGSwipeTableCell
I have been scratching my head for a while on this one. I couldn't find any solution so far, so I decided to create a question. I appreciate the help.
My problem:
My table view header gets into delete mode with no value, when I try to delete any arbitrary cell in my table view and gets removed along with the cell that I am deleting. I am not using a reusable cell and I am not sure what causing this problem.
This is what the table view header currently looks like:
And This is what I want to achieve:
As for the code:
This is how I am setting my table view header:
- (void)viewDidLoad
{
[super viewDidLoad];
self.containerTableView.allowsMultipleSelectionDuringEditing = NO;
if (self.isCorrectDataType)
{
UITableViewCell *cell = [[UITableViewCell alloc]initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.view.frame), 44)];
cell.backgroundColor = [UIColor blackColor];
self.containerTableView.tableHeaderView = cell;
}
}
This is how I am enabling the editing mode and deleting a cell:
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
{
return [self.data count] > 0 ? YES : NO;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete)
{
[self.data removeObjectAtIndex:indexPath.row];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.data];
[self.defaults setObject:data forKey:#"userContent"];
[self.defaults synchronize];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
}
You should not use UITableCell for the custom header. Try using a UIView for that. See the example in this answer: Customize UITableView header section
Issue: I only want to show the selected cells with checkmark. I don't want the grey highlight.
I tried:
cell.selectionStyle = UITableViewCellSelectionStyleNone
but didn't work.
Here is the code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
ProfileSelection *profile = [self.profileSelections objectAtIndex:indexPath.row];
cell.textLabel.text = [profile profileName];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.profileSelectionsTableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
[tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
ProfileSelection *profile = [self.profileSelections objectAtIndex:indexPath.row];
self.mobileProfileId = [profile.profileId stringValue];
[_continueButton setEnabled:YES];
}
How about this:
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
The selectionStyle property should be enough to remove the highlight.
If you want to show the checkmark after the used has selected the row, you need to implement the tableView:didSelectRowAtIndexPath: method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self.tableView cellForRowAtIndexPath:visibleIndexPath].accessoryType = UITableViewCellAccessoryCheckmark;
}
In Swift, the easiest way is to deselect a cell without animation right after it has been selected:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: false)
}
delete your this function
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
Try to use this your problem will be solved
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *simpleTableIdentifier = #"SimpleTableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone
cell.textLabel.text = #"Checking Table View";
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
cell.accessoryType = UITableViewCellAccessoryCheckmark;
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
Go to Storyboard. Select your Table View Cell. Set Selection to None. Then add below codes:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryCheckmark;
}
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView cellForRowAtIndexPath:indexPath].accessoryType = UITableViewCellAccessoryNone;
}
This thread looks a bit old, but I actually came across a decent solution for this recently. In case anyone stumbles across this thread, I figure I might as well post an answer. Especially since I spent hours trying to track this down.
I can't find my original sources anymore, but here's what I found out:
The highlight is made by a background view on the UITableViewCell on the selectedBackgroundView property. If we have a subclass of the table cell, we can detect the transition to the editing state with willTransitionToState: and set the background view to one that is clear/white instead of the blue/gray styles.
Here's what I ended up doing, and it works pretty well.
- (void)willTransitionToState:(UITableViewCellStateMask)state {
[super willTransitionToState:state];
// To remove the blue/gray highlight on the table cell when selected,
// set the selection background to a clear view.
if (state & UITableViewCellStateShowingEditControlMask) {
// If we're moving to an edit state, set the custom background view.
UIView *bgView = [[UIView alloc] init];
[bgView setTintColor:[UIColor whiteColor]];
[bgView setBackgroundColor:[UIColor clearColor]];
[self setSelectedBackgroundView:bgView];
} else {
// Otherwise, just remove it completely.
// The system should handle the rest.
[self setSelectedBackgroundView:nil];
}
}
The plus side to this over the other solutions I've seen is that this one is quite minimal. No need for tracking instance variables or anything else.
I have a table view & a custom cell. the cell contains 3 buttons (check box type) . on button click the respective buttons text i need to change (check / uncheck).
I achieved this, but when i click 1st button on top cell and scroll down the new cell at the bottom also has this check mark, and when i scroll back to top the check mark is moved to next cell.. how to fix this??
code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *strCellIdentifier = #"RemoteCell";
RemoteCustomCell *cell = (RemoteCustomCell*)[tableView ![dequeueReusableCell][2]WithIdentifier:strCellIdentifier];
if (cell == nil) {
cell = [[RemoteCustomCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:strCellIdentifier];
}
else {
cell = [cell initWithStyle:UITableViewCellStyleDefault reuseIdentifier:strCellIdentifier];
}
[cell.btnCheck1 addTarget:self action:#selector(CheckButton1_Click:) forControlEvents:UIControlEventTouchUpInside];
}
return cell;
}
- (void)CheckButton1_Click:(UIButton*)sender
{
RemoteControllCustomCell *clickedCell = (RemoteControllCustomCell *)[[sender superview] superview];
if(clickedCell.btnCheck1.selected)
{
[clickedCell.btnCheck1 setTitle:#"O" forState:UIControlStateNormal];
clickedCell.btnCheck1.selected = NO;
}
else
{
[clickedCell.btnCheck1 setTitle:#"X" forState:UIControlStateSelected];
clickedCell.btnCheck1.selected = YES;
}
}
screenshot:
In your RemoteCustomCell.m file you should implement
- (void)prepareForReuse
{
[super prepareForReuse];
cell.btnCheck1.selected = NO;
}
This way every cell that is reused will have it's btnCheck1.selected value set to NO, and when you load your cell in cellForRowAtIndexPath it will only set it to YES when the cell comes visible and you set it to that.
But it is key to store all your values in an NSMutableArray. There is no such thing as storing your values in the cells only, they get reused on a basis that can not be foreseen. Add your values to the array and use [myArray objectAtIndex:indexPath.row]; to open those values in a cell.
An example:
Somewhere in viewDidLoad
NSMutableArray *myArray = [[NSMutableArray alloc] initWithObjects:#"1", #"0", #"1", #"1", nil];
In your cellForRowAtIndexPath
BOOL yesOrNo = [[myArray objectAtIndex:indexPath.row] boolValue];
And then set your button.selected to the boolean.
This is a typical issue, where you are relying on the UI to the job of your model. The model, the thing that you should pass to your UITableViewCell, so it can be built, would tell it, if it should be displaying an "X" or an "O". Since you are not doing this, the easiest solution, would be to simply reset the state of the cell everytime it gets dequeued.
I think you need to store the state in a array and check the state in
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
CheckButton1_Click do change the state, but when dequeueReusableCell , it loads from cellForRowAtIndexPath again.
It seems like you have dequeue of cell issue. You may implement cellForRowAtIndexPath method as below.
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"RemoteCell";
RemoteCustomCell *cell = (RemoteCustomCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell==nil) {
NSArray *arrNib=[[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
cell= (RemoteCustomCell *)[arrNib objectAtIndex:0];
}
[cell.btnCheck1 addTarget:self action:#selector(CheckButton1_Click:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
[Edited Post]
For practicing, I developing a contact app without any backend. I followed only one NSMutableArray for maintaining contacts which is displayed in a UITableView.
Objective;
Like to search a contact (Ex: Dad), user given a name in a textfield and touch up a button
In the button action, check if Dad contain in the Mutable array which one i followed.
If present, then it definitely present in the tableView as a row. So i want to put selectionStyle for that row.
If search text changes means (Ex: Mom), then revert previous selectionStyle and apply to the row(Mom).
If not present, label notify "Not Present" as text. (Done!).
IBAction:
-(IBAction)actionSearchNameInTable:(id)sender{
if([myContactsArray containsObject:txtForContactName.text]){
myTable.tag=5;
NSInteger tempIndex=[myContactsArray indexOfObject:txtForContactName.text];
NSIndexPath *tempIndexPath=[NSIndexPath indexPathForRow:tempIndex inSection:0];
[self tableView:myTable didSelectRowAtIndexPath:tempIndexPath]; //broke here
NSLog(#"Call Passed!");
}
}
didSelectRowAtIndexPath:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
if(myTable.tag==5){
NSLog(#"called done!");
//Here i like to select that row and apply selectionStyle or favorite background color for that cell, idea?.
myTable.tag=0;
}
These are all just other part of my application. I sured that, Delegate and Datasource are connected properly.
You are calling didDeselectRowAtIndexPath and not didSelectRowAtIndexPath.
And I think you have not implemented didDeselectRowAtIndexPath in your class so thats the reason of crash.
and for your another question:
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if(cell.tag==100)
{
cell.contentView.backgroundColor=[UIColor grayColor];
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell=[tblviw cellForRowAtIndexPath:indexPath];
cell.tag=100;
cell.contentView.backgroundColor=[UIColor grayColor];
}
Change this:
-(IBAction)actionSearchNameInTable:(id)sender{
if([myContactsArray containsObject:txtForContactName.text]){
myTable.tag=5;
NSInteger tempIndex=[myContactsArray indexOfObject:txtForContactName.text];
NSLog(#"TEmp index:%i",tempIndex);
NSIndexPath *tempIndexPath=[NSIndexPath indexPathForRow:tempIndex inSection:0];
NSLog(#"Temp IP:%#",tempIndexPath);
[self tableView:myTable didSelectRowAtIndexPath]; //broke here
NSLog(#"Call Passed!");
}
}
And for custom selection background do this in your cellForRowAtIndexPath.
UIView *customColorView = [[UIView alloc] init];
customColorView.backgroundColor = [UIColor colorWithRed:180/255.0
green:138/255.0
blue:171/255.0
alpha:0.5];
cell.selectedBackgroundView = customColorView;
Hope this helps.. :)
EDIT:
didSelectRowAtIndexPath is a delegate. It only get called when user interaction happens. I face a same problem some days ago. I did it in cellForRowAtIndexPath. For selecting a row do this:
if ([[myContactsArray objectAtIndex:indexPath.row] isEqualToString:txtForContactName.text]) {
[self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
Please try to see my edited answer...
-(IBAction)actionSearchNameInTable:(id)sender{
if([myContactsArray containsObject:txtForContactName.text]){
myTable.tag=5;
NSInteger tempIndex=[myContactsArray indexOfObject:txtForContactName.text];
NSLog(#"TEmp index:%i",tempIndex);
NSIndexPath *tempIndexPath=[NSIndexPath indexPathForRow:tempIndex inSection:0];
NSLog(#"Temp IP:%#",tempIndexPath);
[self tableView:myTable didSelectRowAtIndexPath:tempIndexPath]; //Change here......
NSLog(#"Call Passed!");
}
}
Action for searching that if given text is present or not:
-(IBAction)actionSearchNameInTable:(id)sender{
[txtForContactName resignFirstResponder];
if([myContactsArray containsObject:txtForContactName.text]){
myTable.tag=5;
[myTable reloadData];
}
}
In cellForRowAtIndexPath:
//Better delegate to apply selection style
//Code after dequeue...
myTableCell.textLabel.text=[myContactsArray objectAtIndex:indexPath.row];
myTableCell.imageView.image=[myContactsPhotosArray objectAtIndex:0];
{
myTableCell.textLabel.text=[myContactsArray objectAtIndex:indexPath.row];
myTableCell.imageView.image=[myContactsPhotosArray objectAtIndex:0];
if(myTable.tag==5){
NSLog(#"Ready to apply selectionStyle");
if ([[myContactsArray objectAtIndex:indexPath.row] isEqualToString:txtForContactName.text]) {
[myTable selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
myTable.tag=0;
}
}
return myTableCell;
}