I am using a UISwitch subclass to add UISwitches to all my UITableViewCells. I use the custom class to be able to pass more info to the UISwitch.
The error I have on iOS 8 ONLY is:
*** -[NamedUISwitch _sendActionsForEvents:withEvent:]: message sent to deallocated instance
NamedUISwitch is the Custom UISwitch I made:
#interface NamedUISwitch : UISwitch
#property (nonatomic, strong) NSString *specialinfo1;
#property (nonatomic, strong) NSString *specialinfo2;
#end
#implementation NamedUISwitch
#end
This is how I implement my UISwitch in the cellForRowAtIndexPath method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [[UITableViewCell alloc] init];
NamedUISwitch *switchview = [[NamedUISwitch alloc] initWithFrame:CGRectZero];
[switchview addTarget:self action:#selector(updateSwitchAtIndexPath:) forControlEvents:UIControlEventTouchUpInside];
cell.textLabel.text = ...;
switchview.nomEtablissement = ...;
switchview.tag = ...;
switchview.typeInfo = ...;
cell.accessoryView = switchview;
return cell;
}
I have tried using Instruments to track the dealloc but I can't seem to get it to work the right way.
How can I resolve this dealloc issue?
You are not creating your cells correctly. You need code something like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cell"];
NamedUISwitch *switchview = nil;
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
switchview = [[NamedUISwitch alloc] initWithFrame:CGRectZero];
[switchview addTarget:self action:#selector(updateSwitchAtIndexPath:) forControlEvents:UIControlEventTouchUpInside];
cell.accessoryView = switchview;
} else {
switchview = cell.accessoryview;
}
cell.textLabel.text = ...;
switchview.nomEtablissement = ...;
switchview.tag = ...;
switchview.typeInfo = ...;
// You also need to set switchview.on here
return cell;
}
This way you reuse cells properly and each cell only gets one switch.
An even better option would be to create a custom table cell class and that cell class sets up its own switch.
Related
I'm trying to get the indexPath.row of a button clicked inside a tableView row.
When the user clicks this button I get the index.row corresponding to the button very well, but when I add more objects to the source array to create more cells by calling reloadData, the rowButtonClicked in each cell it's no longer giving me the correct indexPath.row in example I press the index 20 and now the printed indexPath.row is 9.
In cellForRowAtIndexPath to add the event to the button:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
lBtnWithAction = [[UIButton alloc] initWithFrame:CGRectMake(liLight1Xcord + 23, 10, liLight1Width + 5, liLight1Height + 25)];
lBtnWithAction.tag = ROW_BUTTON_ACTION;
lBtnWithAction.titleLabel.font = luiFontCheckmark;
lBtnWithAction.tintColor = [UIColor blackColor];
lBtnWithAction.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
[cell.contentView addSubview:lBtnWithAction];
}
else
{
lBtnWithAction = (UIButton *)[cell.contentView viewWithTag:ROW_BUTTON_ACTION];
}
//Set the tag
lBtnWithAction.tag = indexPath.row;
//Add the click event to the button inside a row
[lBtnWithAction addTarget:self action:#selector(rowButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
To do something with the clicked index:
-(void)rowButtonClicked:(UIButton*)sender
{
//Get the index of the clicked button
NSLog(#"%li", (long)sender.tag);
[self doSomething:(long)sender.tag];
}
Constants.h
#define ROW_BUTTON_ACTION 9
Why it is giving an incorrect index when I change the initial items of the tableView? Is there a way to solve this issue?
It looks like you're messing up button tags. Once you set the tag
lBtnWithAction.tag = indexPath.row;
you won't be able to get button correctly with
lBtnWithAction = (UIButton *)[cell.contentView viewWithTag:ROW_BUTTON_ACTION];
(assuming ROW_BUTTON_ACTION is a constant). lBtnWithAction will be nil all the time except when indexPath.row is equal to ROW_BUTTON_ACTION.
I would propose to subclass UITableViewCell, add a button-property there and then just refer to it directly instead of searching by tag. In this case you'll be able to use tags for buttons freely :) –
#interface UIMyTableViewCell : UITableViewCell
#property (nonatomic, strong, nonnull) UIButton *lBtnWithAction;
#end
And then in cellForRowAtIndexPath:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UIMyTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UIMyTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
[cell.lBtnWithAction addTarget:self action:#selector(rowButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
}
cell.lBtnWithAction.tag = indexPath.row;
return cell;
}
You can simply update you line -
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
to
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]
My take on this will be subclassing the UITableViewCell and providing a delegate for it to respond.
code:
//MyCoolTablewViewCell.h
#protocol MyCoolProtocol<NSObject>
-(void)youTouchedMyCoolCell:(MyCoolTableViewCell __nonnull *)myCoolCell;
#end
#interface MyCoolTableViewCell:UITableViewCell
#property(nonatomic, weak,nullable) id<MyCoolProtocol>myCooldelegate;
#end
//MyCoolTablewViewCell.m
#interface MyCoolTableViewCell(){
UIButton *mySuperCoolButton;
}
#end
#implementation MyCoolTablewViewCell
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
if(!(self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])){
return nil;
}
// init your stuff here
// init button
mySuperCoolButton = [UIBUtton buttonWithType:UIButtonTypeCustom];
[mySuperCoolButton addTarget:self action:#selector(tappedMyCoolButton:) forControlEvents:UIControlEventTouchUpInside];
return self;
}
- (void)tappedMyCoolButton:(id)sender{
if([_myCoolDelegate respondToSelector:#selector(youTouchedMyCoolCell:)]){
[_myCoolDelegate youTouchedMyCoolCell:self];
}
}
#end
then in your controller
// whatEverController.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// get the cell
cell = [tableView dequeueReusableCellWithIdentifier:#"myCoolIdentifier"forIndexPath:indexPath];
cell.myCooldelegate = self;
}
-(void)youTouchedMyCoolCell:(MyCoolTableViewCell *)myCoolCell{
NSIndexPath *myCoolIndexPath = [_tableView indexPathForCell:myCoolCell];
// then do whatever you want with the index path
}
I have a UITableView which contains the names of all countries.
The user can delete or edit the name of country anytime by taping on the cell.
My UITableView cell initially looks like this:
Now when user taps on it I am changing it like this:
I think I am following a very lame approach.Here is what I did:
Declared globally buttons to add:
UIButton *btnDeleteWithImage,*btnDeleteWithText,*btnEditWithImage,*btnEditWihtText; //buttons
And a NSMutableArray to keep track of indexPath.row
Now in my didSelectMethod I am doing this:
//To change the background
UIView *selectionBackground = [[UIView alloc] init];
selectionBackground.backgroundColor = [UIColor customColor];
cell.selectedBackgroundView = selectionBackground;
// to check which cell is pressed
if([indexPathCollection containsObject:index])
{
[btnDeleteWithImage removeFromSuperview];
[btnDeleteWithText removeFromSuperview];
[btnEditWihtText removeFromSuperview];
[btnEditWithImage removeFromSuperview];
[indexPathCollection removeObject:index];
[cell addSubview:btnDeleteWithImage];
[cell addSubview:btnDeleteWithText];
[cell addSubview:btnEditWithImage];
[cell addSubview:btnEditWihtText];
[indexPathCollection addObject:index];
}
else
{
[cell addSubview:btnDeleteWithImage];
[cell addSubview:btnDeleteWithText];
[cell addSubview:btnEditWithImage];
[cell addSubview:btnEditWihtText];
[indexPathCollection addObject:index];
}
But this is not working good.When I scroll table edit and delete button randomly occurs.
Did someone has better Idea how can achieve this in a very efficient way.
You can achieve this by creating a custom cell with your properties
CustomCell.h
#interface CustomCell : UITableViewCell
#property (nonatomic, strong) UIButton *btnDeleteWithImage;
#property (nonatomic, strong) UIButton *btnDeleteWithText;
#property (nonatomic, strong) UIButton *btnEditWithImage;
#property (nonatomic, strong) UIButton *btnEditWithText;
#end
initialize them in cell's init method keeping them hidden at first or you can do
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *customCellIdentifier = #"customCellIdentifier";
CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:customCellIdentifier];
if (!cell) {
cell = [[CustomCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:customCellIdentifier];
// or you can initialize and add them to the cell here
}
//here you can modify them accordingly
return cell;
}
then the delegate method can be
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
CustomCell *cell = [tableView cellForRowAtIndexPath:indexPath];
[cell.btnDeleteWithImage setHidden:NO];
[cell.btnEditWithImage setHidden:NO];
}
the simplest way is that do not reuse the cell . & register your custom cell with same indentifire .
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
customCell* cell = [[customCell alloc] initWithStyle: UITableViewCellStyleDefault reuseIdentifier:#"indentifire"] ;
// manage your plooting here ..
return cell;
}
hope it works for you.
I have a custom UITableViewCell who contains 3 UIButtons and I want that when I tap one button, the background of my cell change. But when I tap a button all cells in my UITableView change their background and not only the cell who has the button inside it.
Custom Cell (.h)
#interface SCActionsViewCell : UITableViewCell
#property (nonatomic, weak) UITableView *tableView;
#property (nonatomic, strong) NSIndexPath *indexPath;
#property (nonatomic, strong) UIButton *thanksButton;
#property (nonatomic, strong) UIButton *commentsButton;
#property (nonatomic, strong) UIButton *reButton;
#end
Custom Cell (.m)
#define TEXT_BUTTON_SIZE 25
#implementation SCActionsViewCell
- (void)awakeFromNib {
// Initialization code
self.backgroundColor = [UIColor colorLight];
_thanksButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, self.contentView.frame.size.width/3, actionsCellHeight)];
_commentsButton = [[UIButton alloc] initWithFrame:CGRectMake(self.contentView.frame.size.width/3, 0, self.contentView.frame.size.width/3, actionsCellHeight)];
_reButton = [[UIButton alloc] initWithFrame:CGRectMake(self.contentView.frame.size.width*2/3, 0, self.contentView.frame.size.width/3, actionsCellHeight)];
_thanksButton.titleLabel.font = [UIFont fontWithName:kFontAwesomeFamilyName size:TEXT_BUTTON_SIZE];
_commentsButton.titleLabel.font = [UIFont fontWithName:kFontAwesomeFamilyName size:TEXT_BUTTON_SIZE];
_reButton.titleLabel.font = [UIFont fontWithName:kFontAwesomeFamilyName size:TEXT_BUTTON_SIZE];
[_thanksButton setTitle:[NSString fontAwesomeIconStringForEnum:FAThumbsOUp] forState:UIControlStateNormal];
[_commentsButton setTitle:[NSString fontAwesomeIconStringForEnum:FACommentsO] forState:UIControlStateNormal];
[_reButton setTitle:[NSString fontAwesomeIconStringForEnum:FARetweet] forState:UIControlStateNormal];
[_thanksButton setTitleColor:[UIColor sGreen] forState:UIControlStateSelected];
[_reButton setTitleColor:[UIColor sGreen] forState:UIControlStateSelected];
[self addSubview:_thanksButton];
[self addSubview:_commentsButton];
[self addSubview:_reButton];
}
#end
SCViewDataSource (TableviewDataSource)
-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.posts.count;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 2;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SCActionsViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SCActionsViewCell" forIndexPath:indexPath];
cell.tableView = tableView;
cell.indexPath = indexPath;
[cell.thanksButton addTarget:(SCViewController *)tableView.delegate action:#selector(touched:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
SCViewController.m
-(void)touched:(UIButton *)button{
NSLog(#"indexPath : %#",((SCActionsViewCell *)button.superview).indexPath);
((SCActionsViewCell *)button.superview).backgroundColor = [UIColor blueColor];
}
In this case when I tap thanksButton in a cell all the cells change their background to blueColor...
(Try this) Updated you touched methood using following code.
-(void)touched:(UIButton *)button{
NSLog(#"indexPath : %#",((SCActionsViewCell *)button.superview).indexPath);
((SCActionsViewCell *)button.superview).contentView.backgroundColor = [UIColor blueColor];
}
Ok I found out by my own : don't use [tableView dequeueReusableCellWithIdentifier:forIndexPath:] but [[CustomCell alloc] init] and create your init function in your custom cell class.
Like this every cells will be a new object.
Add a property (maybe an NSDictionary or an NSArray) for tracking which indexPaths have been tapped already. Creating a new cell every time is very expensive. I suggest you don't abandon [tableView dequeueReusableCellWithIdentifier:forIndexPath:].
You could try this:
- (void)touched:(UIButton *)sender {
NSIndexPath *indexPath = ((SCActionsViewCell *)button.superview).indexPath;
[self.thankedIndexPaths addObject:indexPath];
}
Then you can set the background color accordingly:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SCActionsViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"SCActionsViewCell" forIndexPath:indexPath];
cell.tableView = tableView;
cell.thanked = [self.thankedIndexPaths containsObject:indexPath];
cell.indexPath = indexPath;
[cell.thanksButton addTarget:(SCViewController *)tableView.delegate action:#selector(touched:) forControlEvents:UIControlEventTouchUpInside];
return cell;
}
I have been searching this issue for last few days.
In my file .h I put this in the interface:
NSMutableArray *newsCount;
#property (nonatomic, retain) NSMutableArray *newsCount;
And in my file .m I have wrore this piece of code.
I have alloc this in view Will Appear method:
- (void)viewWillAppear:(BOOL)animated{
self.newsCount = [[NSMutableArray alloc] init];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.newsCount count];
// return 5;
}
- (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] autorelease];
}
// Configure the cell.
cell.textLabel.text = [NSString stringWithFormat:#"- %#",[[newsCount objectAtIndex: indexPath.row] objectForKey:#"RELATED_CAPTION"]];
[cell.textLabel setFont:[UIFont fontWithName:#"Helvetica" size:13]];
cell.textLabel.lineBreakMode = UILineBreakModeWordWrap;
cell.textLabel.numberOfLines = 2;
cell.textLabel.textColor = [UIColor grayColor];
return cell;
}
How are you setting self.newsCount?
Either you are not putting an array into self.newsCount, or (more likely) you are setting "newsCount" without retaining it.
Are you using ARC? If not, you should be.
If you are setting newsCount like you mentioned in a comment on #Kendall's answer:
newsCount = [[[GlobalVariable sharedInstance].itemNewsDetail objectAtIndex:i]objectForKey:#"RELATED"];
then the issue could be due to that object not being an NSMutableArray, but an NSString (__NSCFString is a private class used by NSString)
You might want to dump the contents of [GlobalVariable sharedInstance].itemNewsDetail by adding this line right after the line I mentioned earlier:
NSLog(#"itemNewsDetail: %#",[[GlobalVariable sharedInstance].itemNewsDetail objectAtIndex:i]);
and checking to see what is stored at the key #"RELATED"...
I want to use a custom UITableviewCell with UITableView in same xib without creating a UITableViewCell class?
As you can see bellow i set the identifier for UITableViewCell and used it like this:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"CustomIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
}
But Not Working?
Add a UITableViewCell *customCell property to your view controller (for example, your file ShowUsers.h)...
#property (nonatomic, strong) IBOutlet UITableViewCell *customCell;
and connect it to the custom cell in your xib. Then use the following code in cellForRow (for example, your file ShowUsers.m) :
if (cell == nil) {
[[NSBundle mainBundle] loadNibNamed:#"NibName" owner:self options:nil];
cell = self.customCell;
self.customCell = nil;
}
Yes you can do as following code
Under ViewDidLoad or ViewWillAppear
label1Array=[NSMutableArray arrayWithObjects:#"A",#"B",nil];
label2Array=[NSMutableArray arrayWithObjects:#"C",#"D",nil];
label3Array=[NSMutableArray arrayWithObjects:#"E",#"F",nil];
UITableViewDelegate
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"aCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UILabel *label1=[[UILabel alloc]init];
label1.frame=CGRectMake(0,0,40,40);
label1.text=[label1Array objectAtIndex:indexPath.row];
................
[cell.contentView addSubView: label1];
UILabel *label2=[[UILabel alloc]init];
label2.frame=CGRectMake(50,0,40,40);
label2.text=[label2Array objectAtIndex:indexPath.row];
[cell.contentView addSubView: label2];
UILabel *label3=[[UILabel alloc]init];
label3.frame=CGRectMake(100,0,40,40);
label3.text=[label3Array objectAtIndex:indexPath.row];
[cell.contentView addSubView: label3];
}
Hope this Helps !!!
See..You can do like this. Here you are adding two UITableViewCell in one TableView using XIB.
In Class.h file :-
Declare one mutable array for number of cells.
{
NSMutableArray * sectionRows;
}
#property (nonatomic,retain) IBOutlet UITableViewCell *addTvc1;
#property (nonatomic,retain) IBOutlet UITableViewCell *addTvc2;
In Class.m :-
#synthesize addTvc1, addTvc2;
- (void)viewDidLoad
{
[super viewDidLoad];
sectionRows = [[NSMutableArray alloc] init];
[sectionRows addObject:[[NSMutableArray alloc]
initWithObjects:addTvc1, nil]];
[sectionRows addObject:[[NSMutableArray alloc]
initWithObjects:addTvc2, nil]];
}
In UITableViewDelegate Method -
Add This -
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return [sectionRows count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:
(NSInteger)section
{
return [[sectionRows objectAtIndex:section] count];
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:
(NSIndexPath *)indexPath
{
return [[sectionRows objectAtIndex:indexPath.section]
objectAtIndex:indexPath.row];
}
Along with code, In Class's XIB drop and drag two UITableViewCell which should be outside from the view and add those cells with Files Owner.
It will work perfectly. Here no need to create one separate custom UITableViewCell class.